I have a static_assert
in a move constructor of a template struct of mine. Is this static_assert
required to be considered by the compiler, even if copy elision is possible?
This is the stripped-down scenario:
#include <type_traits>
template<typename T>
struct X
{
X(X&&) { static_assert(std::is_same<void, T>::value, "Intentional Failure"); }
};
auto impl() -> X<int>;
auto test() -> decltype(impl())
{
return impl();
}
int main()
{
test();
}
GCC and Clang agree to evaluate the static_assert
and fail to compile.
MSCV and ICC on the other hand compile the code just fine.
Interestingly, when I remove the definition of the move
constructor and just declare it like this:
template<typename T>
struct X
{
X(X&&);
};
GCC and Clang also compile the code now. Thus, all compilers seem to agree that the definition of the move constructor is irrelevant for copy elision.
Question:
If there is a static_assert
in the copy/move constructor, does the standard require it to be evaluated even if copy/move elision is possible?
The following should help.
You do not have to employ type deduction to illustrate the problem. Even the simpler example has the same issue:
#include <type_traits>
template <typename T>
struct X
{
X() {}
X(X&&) { static_assert(std::is_same<void, T>::value, "failed"); }
};
int main()
{
X<int> x = X<int>();
}
Clang and GCC will not compile it. MSVC compiles and executes fine.
This shows that the problem is related to odr-use and when definitions of member functions are instantiated.
14.7.1 [temp.inst] paragraph 2 says "[...] the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist"
3.2 [basic.def.odr] paragraph 3 says (in a note) "[...] A constructor selected to copy or move an object of class type is odr-used even if the call is actually elided by the implementation"
3.2 [basic.def.odr] paragraph 4 says "Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required."
Ergo: the specialization should be instantiated, and the assertion have fired.
I believe the answer is no. My logic goes like this:
References:
14.7.1.1 …The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions…
14.7.1.2 Unless a member of a class template… has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist…
move constructors are not called. static_assert
is evaluated upon instantiation of X<int>::X(X&&)
. Most probably, some compilers evaluate template methods upon use (when you use move constructor, and you don't use it), and others - upon instantiation of class template (when you first use X<int>
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With