It's a wrong expectation. To start with, a class template has only one point of instantiation in a translation unit:
[temp.point]
7 ... A specialization for a class template has at most one point of
instantiation within a translation unit. A specialization for any
template may have points of instantiation in multiple translation
units. If two different points of instantiation give a template
specialization different meanings according to the one-definition
rule, the program is ill-formed, no diagnostic required.
Templates never allowed for two points in the program to have a different interpretation of the template for the same set of arguments (an ODR nightmare in the general case). You basically start venturing into nasal-demon territory with your attempt at the trait.
And should you think using C++20 concepts is gonna change anything, you'll dive right into ill-formed; no diagnostic required territory if you conceptify the example
template<typename T>
concept IsComplete = requires{sizeof(T);};
int main()
{
struct A;
static_assert(!IsComplete<A>); // ok
struct A{};
static_assert(IsComplete<A>); // error or nuclear launch.
}
[temp.names]
8 ... A concept-id evaluates to true if the concept's normalized
constraint-expression is satisfied ([temp.constr.constr]) by the
specified template arguments and false otherwise.
[temp.constr.atomic]
3 ... If, at different points in the program, the satisfaction
result is different for identical atomic constraints and template
arguments, the program is ill-formed, no diagnostic required.
It's not anything new, concepts just add more of the same. A template's meaning for a specific set of arguments must not change if some property of the arguments is different in two different points in the program.
So while a concept (even in pre-C++20 SFINAE hackery) that checks if a type is complete may be written, to use it carelessly is to play with fire.
struct
is created forA
and is used in both cases. – Untangle