Using parameter pack expansion with rvalue references and const class member data causes a link error on Clang 5 and 6, and GCC 4.8.5 and 5.3. I first encountered the problem with a function with the same signature as std::make_shared
but have simplified it to the following test case showing 5 different ways that I would expect to work.
#include <utility>
constexpr int global_value = 3;
struct Foo {
static constexpr int value = 3;
};
struct Bar {
static const int value = 3;
};
template<class... Args>
int* make(Args&&... args)
{
return new int(std::forward<Args>(args)...);
}
template<class... Args>
int* make_nofwd(Args&&... args)
{
return new int(args...);
}
template<class... Args>
int* make_norvalue(Args... args)
{
return new int(args...);
}
int main(int, char** )
{
#if ATTEMPT == 0
int* v = make(Foo::value); // FAIL
#elif ATTEMPT == 1
int* v = make_nofwd(Foo::value); // FAIL
#elif ATTEMPT == 2
int* v = make_norvalue(Foo::value); // OK
#elif ATTEMPT == 3
int* v = make(global_value); // OK
#elif ATTEMPT == 4
int* v = make(Bar::value); // OK
#endif
return (*v == 3 ? 0 : 1);
}
Running each attempt shows that the struct's constant and constexpr data aren't getting inserted when passed as rvalues.
$ for i in {0..4}; do echo "=== Attempt $i ==="; g++ derp.cc -Wall -Wextra -std=c++11 -D ATTEMPT=$i -o derp.exe && ./derp.exe && echo "SUCCESS" ; done
=== Attempt 0 ===
/tmp/ccC5TyiZ.o: In function `main':
derp.cc:(.text+0x10): undefined reference to `Foo::value'
collect2: error: ld returned 1 exit status
=== Attempt 1 ===
/tmp/ccCwlV85.o: In function `main':
derp.cc:(.text+0x10): undefined reference to `Foo::value'
collect2: error: ld returned 1 exit status
=== Attempt 2 ===
SUCCESS
=== Attempt 3 ===
SUCCESS
=== Attempt 4 ===
/tmp/ccnfzmHk.o: In function `main':
derp.cc:(.text+0x10): undefined reference to `Bar::value'
collect2: error: ld returned 1 exit status
And, I just discovered that running with any optimization magically fixes it:
for i in {0..4}; do echo "=== Attempt $i ==="; $CXX derp.cc -Wall -Wextra -std=c++11 -D ATTEMPT=$i -O2 -o derp.exe && ./derp.exe && echo "SUCCESS" ; done;
=== Attempt 0 ===
SUCCESS
=== Attempt 1 ===
SUCCESS
=== Attempt 2 ===
SUCCESS
=== Attempt 3 ===
SUCCESS
=== Attempt 4 ===
SUCCESS
Am I somehow using unspecified behavior here? Why isn't Args&&
passing the int
through by value?
Aucun commentaire:
Enregistrer un commentaire