I want to write a class template M
that accepts incomplete type C
as template parameter.
But I also want C
has some traits when it is eventually defined.
Is this code guaranteed
- to compile if defined(FLAG),
- and to fail the compiling if !defined(FLAG)?
template <auto> struct Dummy {};
template <typename C>
void check()
{
static_assert(std::is_trivial_v<C>);
}
template <typename C>
struct M : Dummy<&check<C>>
{
//static_assert(std::is_trivial_v<C>);//error: incomplete type
C * p;
};
struct Test;
M<Test> m;
int main()
{
return 0;
}
#if defined(FLAG)
struct Test {};
#else
struct Test { std::string non_trivial_member; };
#endif
From a n4713,
Point of instantiation [temp.point] (17.7.4.1/8)
First, note that the primary template being paa0ssed arguments a specialization in standard speak. C++ programmers use it differently than the standard does in my experience.
Second,
check<Test>
has two points of instantiation in your program; once atand once at the end of the translation unit.
The meaning of
check<Test>
atM<Test> m
is different than the meaning ofcheck<Test>
at the end of the translation unit. At one spot,Test
is incomplete, at the other it is complete. The body ofcheck<Test>
definitely has a different meaning.So your program is ill-formed, no diagnostic required. In your case, the ill-formed program happens to do what you want, but it could (under the standard) compile to anything at all, or fail to compile.
I suspect the reason behind this rule is to give the compiler the freedom to instantiate
check
either immediately or to defer it until later. You are not allowed to rely on which of the two spots it actually does the instantiation of the body ofcheck
.