out of line definition of guided constructor (c++20) - c++

g++ happily accepts the following code, whereas clang nor msvc are able to match out of line definitions.
Any idea why ?
template <bool B>
struct test
{
test() requires (B);
test() requires(!B);
};
template <>
test<true>::test()
{}
template <>
test<false>::test()
{}
int main()
{
test<false> a;
test<true> b;
return 0;
}
Demo
Clang:
error: out-of-line definition of 'test' does not match any declaration in 'test<true>'
Msvc:
error C2244: 'test<true>::test': unable to match function definition to an existing declaration

You're declaring constrained constructors but defining two unconstrained specializations. Those won't ever match.
What you probably meant was:
template <bool B>
struct test
{
test() requires (B);
test() requires(!B);
};
template <bool B>
test<B>::test() requires (B)
{}
template <bool B>
test<B>::test() requires (!B)
{}
This compiles fine in all 3 compilers.
As to why your original version compiles - it's a GCC bug 96830. Clang is right, code is ill-formed because the out-of-line definitions don't match the template definition (note also that template<> ... is full specialization syntax).
See [temp.class.general]/3 (emphasis mine):
When a member of a class template is defined outside of the class template definition, the member definition is defined as a template definition with the template-head equivalent to that of the class template.
[temp.over.link]/6:
Two template-heads are equivalent if their template-parameter-lists have the same length, corresponding template-parameters are equivalent and are both declared with type-constraints that are equivalent if either template-parameter is declared with a type-constraint, and if either template-head has a requires-clause, they both have requires-clauses and the corresponding constraint-expressions are equivalent.
Also see [temp.mem.func]/1 for an example of declaring a constrained member out-of-line:
template<typename T> struct S {
void f() requires C<T>;
void g() requires C<T>;
};
template<typename T>
void S<T>::f() requires C<T> { } // OK
template<typename T>
void S<T>::g() { } // error: no matching function in S<T>

Related

Out of class definition of member function template in the presence of a member function of the same name - compiler divergence

Consider this example of a class template containing a member function and a member function template both of which are named f followed by an attempt to define and specialize the member function template:
template<typename T>
struct A {
void f();
template<typename U>
void f();
};
template<>
template<typename U>
void A<double>::f() { }
template<>
template<>
void A<double>::f<int>() { }
Is this legal? How should the member function template definition be written?
In the first definition of f, does the template declaration ensure that the member function template f should be selected rather than the member function f even though the template parameter does not appear in the type? I don't see any way to further disambiguate this as writing ::template f(), ::f<U>(), ::f<>(), or a combination thereof does not seem to help.
Compiler results:
clang: crashes with a stack trace at the specialization of f<int>
Visual Studio: compiles successfully
gcc: compile error (ambiguity regarding which f is being defined) at the first definition of f
EDG: compiles successfully
By contrast, every compiler accepts the equivalent at namespace scope:
namespace X {
void f();
template<typename T>
void f();
}
template<typename T>
void X::f() { }
template<>
void X::f<int>() { }
This is a language defect report DR 1665, which is in drafting status at this moment.
As mentioned there the standard says
A normal (non-template) member function with a given name and type and a member function template of the same name, which could be used to generate a specialization of the same type, can both be declared in a class. When both exist, a use of that name and type refers to the non-template member unless an explicit template argument list is supplied.
And there is an open GCC bug report with an example very similar to yours: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64063

C++: Why does the compiler instantiate a template when it is not used

Let's consider this code:
template <bool c>
class A {
public:
A() = default;
// I want to enable f(int) only if c == true
template<typename Temp = typename enable_if<c>::type>
void f(int val) {
cout << val << endl;
};
};
int main() {
A<false> a;
A<true> b;
b.f(543);
}
When I try to compile this I get the following error:
error: no type named 'type' in 'struct std::enable_if<false, void>'
But I don't use the template method f(int) when the argument <bool c> is false then it shouldn't exist.
The compiler does not "instantiate" your template, as you seem to incorrectly believe. The compiler is simply trying to parse and analyze your template declaration, which is a part of your class definition. If the class is instantiated, then all member declarations have to be valid. Your member template declaration is not valid. Hence the error.
If some template is "not used", it means that it does not get specialized and instantiated. But the declaration of that template still has to be valid. And the validity of those parts of that declaration that do not depend on template parameters is checked immediately. In other words, what you wrote in your code is no different from
template <typename T = jksgdcaufgdug> void foo() {}
int main() {}
or, closer to your situation
template <typename T = std::enable_if<false>::type> void foo() {}
int main() {}
Even though these programs do not "use" (do not instantiate) function template foo, it still does not mean that the declaration of foo can contain random garbage like jksgdcaufgdug or explicitly refer to non-existent entities like std::enable_if<false>::type. The above examples will not compile for that reason.
You can use "random garbage" in dependent contexts, like
template <typename T> void foo(typename T::kjhdfjskhf x)
{
typename T::jksgdcaufgdug i;
}
and you can use std::enable_if in dependent contexts like
template <typename T,
typename U = typename enable_if<is_void<T>::value>::type>
void bar()
{
}
and it will not produce "early" errors, but in your case enable_if<c> does not depend on Temp, so it is not in dependent context. Which means that the correctness of typename enable_if<c>::type is checked immediately when you instantiate A<false>.

Specialization of member function template after instantiation error, and order of member functions

The following bit of code fails to compile on gcc 4.5.3
struct Frobnigator
{
template<typename T>
void foo();
template<typename T>
void bar();
};
template<typename T>
void Frobnigator::bar()
{
}
template<typename T>
void Frobnigator::foo()
{
bar<T>();
}
template<> // error
void Frobnigator::foo<bool>()
{
bar<bool>();
}
template<>
void Frobnigator::bar<bool>()
{
}
int main()
{
}
Error message: specialization of ‘void Frobnigator::bar() [with T = bool]’ after instantiation. I finally resolved this problem by having the specialization of Frobnigator::bar<bool>() appear before Frobnigator::foo<bool>(). Clearly the order in which the methods appear matter.
Why then is the following lite version of the above code, in which the the specialization of bar appears after the generic version, valid ?
struct Frobnigator
{
template<typename T>
void foo();
};
template<typename T>
void Frobnigator::bar()
{
}
template<>
void Frobnigator::bar<bool>()
{
}
int main()
{
}
Your first code is not correct by standard.
n3376 14.7.3/6
If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation
to take place, in every translation unit in which such a use occurs; no diagnostic is required.
In your case - implicit instantiation of bar function with type bool is required by its usage in foo<bool>, before explicit specialization declaration.
Clearly the order in which the methods appear matter.
Indeed; as is usually the case in C++, you can't use something before it's declared, and this applies to explicit template specialisations as well as most other things.
Using bar<bool> (by calling it from foo<bool>) without a previous declaration of an explicit specialisation causes that specialisation to be instantiated from the generic template, if it hasn't already been. You'll need at least a declaration of the explicit specialisation to prevent that.
Why is this the case, considering that the specialization of bar appears after the generic version in the following lite version of the above code
The second example differs by not instantiating foo<bool> at all. The issue isn't that the specialisation is declared after the generic template (which must be the case), but that it's declared after that specialisation has already been instantiated.

How are template definitions matched to template declarations?

How exactly is a template declaration matched to a template definition? I found some text in the standard about template-ids referring to the same function if "their template-names [...] refer to the same template and [...]" (14.4 [temp.type] p1) but I can't find a definition for template-names or when template-names refer to the same template. I'm not sure if I'm on the right track anyway because I haven't deciphered the grammar well enough to tell if a template-id is part of the definition/declaration of a template, or just a use of a template.
For example, the following program works fine.
#include <iostream>
template<typename T>
T foo(T t);
int main() {
foo(1);
}
template<typename T>
T foo(T t)
{ std::cout << "A\n"; return 0; }
If I change the way I use the template parameters in the template definition the names apparently no longer refer to the same template, and linking fails.
#include <iostream>
template<typename T>
T foo(T t);
int main() {
foo(1);
}
template<typename T>
int foo(T t) { std::cout << "A\n"; return 0; }
// or
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
typename identity<T>::type
foo(T t) { std::cout << "A\n"; return 0; }
Next, if I move the template definition to another translation unit, for my implementation of C++ (MSVC 11 beta) the program works no matter how I say the types.
//main.cpp
template<typename T>
T foo(T t);
int main() {
foo(1);
}
//definition.cpp
#include <iostream>
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
typename identity<T>::type
foo(T t) { std::cout << "A\n"; return 0; }
template int foo<int>(int);
or
//definition.cpp
#include <iostream>
template<typename T>
int foo(T t) { std::cout << "A\n"; return 0; }
template int foo<int>(int);
or even if the definition isn't a template at all:
//definition.cpp
#include <iostream>
int foo(T t) { std::cout << "A\n"; return 0; }
Obviously linking is succeeding because the signature/mangled name is the same regardless of the template that was instantiated to create the symbol. I think this undefined behavior because I'm violating:
§ 14.1 [temp] p6
A function template, member function of a class template, or static
data member of a class template shall be defined in every translation
unit in which it is implicitly instantiated (14.7.1) unless the
corresponding specialization is explicitly instantiated (14.7.2) in
some translation unit; no diagnostic is required.
But then say I try to fulfill those requirements by putting a definition of the template in the second translation unit, and including an explicit instantiation at one of two locations:
#include <iostream>
template<typename T>
T foo(T t) { std::cout << "A\n"; return 0; }
// Location 1
template<typename T>
int foo(int t) { std::cout << "B\n"; return 0; }
// Location 2
What are the rules about disambiguating what template an explicit instantiation refers to? Putting it at Location 1 causes the correct template to be instantiated and that definition to be used in the final program, while putting it at Location 2 instantiates the other template, and causes what I believe is undefined behavior under 14.1 p6 above.
On the other hand an implicit instantiation of two templates definitions picks the first template no matter what, so it seems like the rule for disambiguating the templates is different in these circumstances:
#include <iostream>
template<typename T>
T foo(T t) { std::cout << "A\n"; return 0; }
template<typename T>
int foo(int t) { std::cout << "B\n"; return 0; }
int main() {
foo(1); // prints "A"
}
The reason this came up is related to this question where the questioner discovered that a single forward declaration
template<typename T>
T CastScriptVarConst(const ScriptVar_t& s);
could not act as a declaration of multiple template definitions:
template<typename T>
typename std::enable_if<GetType<T>::value < SVT_BASEOBJ,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
return (T) s;
}
template<typename T>
typename std::enable_if<!(GetType<T>::value < SVT_BASEOBJ)
&& std::is_base_of<CustomVar,T>::value,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
return *s.as<T>();
}
And I wanted to better understand the relationship between template definitions and declarations.
OK, let's start from the beginning. The "template-name" of a template is the actual name of the function or class being templated; that is, in
template<class T> T foo(T t);
foo is the template name. For function templates, the rule for deciding whether they are the same is quite long, described in 14.5.5.1 "Function template overloading". Paragraph 6 of that section (I'm quoting from C++03 here, so the wording and paragraph numbers might have changed in C++11) defines the terms equivalent and functionally equivalent, when applied to expressions involving template parameters.
In short, equivalent expressions are the same apart from possibly having different names for template parameters, and functionally equivalent expressions are the same if they happen to evaluate to the same thing. For example, the first two f declarations are equivalent, but the third is only functionally equivalent to the other two:-
template<int A, int B>
void f(array<A + B>);
template<int T1, int T2>
void f(array<T1 + T2>);
template<int A, int B>
void f(array< mpl::plus< mpl::int<A>, mpl::int<B> >::value >);
It goes on in paragraph 7 to extend those two definitions to whole function templates. Two function templates that match (in name, scope, and template parameter lists) are equivalent if they also have equivalent return types and argument types, or functionally equivalent if they only have functionally equivalent return types and argument types. Looking at your second example, these two functions are only functionally equivalent:-
template<typename T>
T foo(T t);
template<typename T>
typename identity<T>::type foo(T t);
Paragraph 7 closes with the dire warning that, "If a program contains declarations of function templates that are functionally equivalent but not equivalent, the program is ill-formed; no diagnostic is required." Your second example is, thus, not valid C++. Detecting errors like that would require each declaration and definition of a function template to be annotated in the binary with an AST describing the template expression each parameter and the return type came from, which is why the standard doesn't require implementations to detect it. MSVC is justified in compiling your third example how you intended, but it would be just as justified to break.
Moving on to explicit instantiation, the important section is 14.7, "Template instantiation and specialization". Paragraph 5 disallows all of the following:
Explicitly instantiating a template more than once;
Explicitly instantiating and explicitly specializing the same template;
Explicitly specializing a template for the same set of arguments more than once.
Again, "no diagnostic is required" as it's quite hard to detect.
So to expand your explicit instantiation example, the following code breaks the second rule and is illegal :-
/* Template definition. */
template<typename T>
T foo(T t)
{ ... }
/* Specialization, OK in itself. */
template< >
int foo(int t)
{ ... }
/* Explicit instantiation, OK in itself. */
template< >
int foo(int t);
This is illegal regardless of the locations of the explicit specialization and the explicit instantiation, but of course because no diagnostic is required, you may get useful results on some compilers. Note also the difference between explicit instantiation and explicit specialization. The following example is ill-formed because it declares an explicit specialization without defining it:-
template<typename T>
T f(T f)
{ ... }
template< >
int f(int);
void g(void)
{ f(3); }
but this example is well-formed, because it has an explicit instantiation:-
template<typename T>
T f(T f)
{ ... }
template f(int);
void g(void)
{ f(3); }
The < > makes all the difference. Be warned also that even when you do define an explicit specialization, it has to be before you use it, otherwise the compiler might already have generated an implicit instantiation for that template. This is in 14.7.3 "Explicit specialization" paragraph 6, just below where you were reading, and again, no diagnostic is required. To adapt the same example, this is ill-formed:-
template<typename T>
T f(T f)
{ ... }
void g(void)
{ f(3); } // Implicitly specializes int f(int)
template< >
int f(int) // Too late for an explicit specialization
{ ... }
If you weren't confused enough yet, take a look at your last example:-
template<typename T>
T foo(T t) { ... }
template<typename T>
int foo(int t) { ... }
The second definition of foo is not a specialization of the first definition. It would have to be template< > int foo(int) to be a specialization of template<typename T> T foo(T). But that's OK: function overloading is allowed, and it's allowed between function templates and normal functions. Calls of the form foo(3) will always use the first definition, because its template parameter T can be deduced from the argument type. The second definition does not allow its template parameter to be deduced from the argument type. Only by explicitly specifying T can you reach the second definition, and only then when the call is not ambiguous with the first definition:-
f<int>(3); // ambiguous
f<string>(3); // can only be the second one
The whole process of doing overload resolution for function templates is too long to describe here. Read section 14.8.3 if you're interested and ask more questions :-)

... used without template parameters error

I am in need of your help once again...
I had the following code, which was causing explicit specialization in non-namespace scope error:
namespace __test
{
template <int A, int B, typename C> class Test
{
template <int V> void check(C & a) { }
template <> void check<0>(C & a) { } //error: explicit specialization in non-namespace scope 'class __test::Test<A, B, C>'
};
}
Since I already know how to fix this kind of errors, I defined specialization outside of the class scope, however I got another error - ... used without template parameters:
namespace __test
{
template <> void Test::check<0>(C & a) { } //error: 'template<int A, int B, class C> class __test::Test' used without template parameters
}
I'm probably just being stupid, but I don't understand the cause of this problem and I don't know how to fix it... Please help!
By my reading of the standard, what you want to do appears to be legal. Quoting §14.7.3/18:
In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well. In such explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member. The types of the template-parameters in the template-parameter-list shall be the same as those specified in the primary template definition.
As you're explicitly specializing a member function template rather than a class member template, it should be fine; however, neither Comeau, GCC, nor VC++ allow the following, which should be correct syntax:
namespace test
{
template<int A, int B, typename C>
class Test
{
template<int V>
void check(C& a) { }
};
template<int A, int B, typename C>
template<>
void Test<A, B, C>::check<0>(C& a) { }
}
Comeau says error: a template declaration containing a template parameter list may not be followed by an explicit specialization declaration, which makes sense if we apply the rule in §14.7.3/18 to member function templates as well
GCC says invalid explicit specialization before '>' token; enclosing class templates are not explicitly specialized, which again makes sense if we apply the rule in §14.7.3/18 to member function templates as well
VC++ says error C2768: 'test::Test<A,B,C>::check' : illegal use of explicit template arguments, which isn't a helpful error message, but generally falls in line with the others
My guess is that there must be a defect report filed that also disallows explicit specializations of member function templates when the enclosing class templates are not explicitly specialized as well; however, I can't say this definitively since the wording for §14.7.3/18 hasn't changed between the C++03 standard and the C++0x FDIS (which it would if a DR was filed against C++03 and accepted).
You need to either fully specialize everything, like this:
namespace __test {
template <int A, int B, typename C>
class Test
{
template <int V> void check(C & a) { }
};
template <>
template <>
void Test<1, 2, int>::check<0> (int &)
{
}
}
Or use a helper structure to avoid trying to partially specialize template method of a template class (which GCC and many others won't understand):
namespace __test {
template <typename C, int V>
struct TestHelper
{
static void check (C & a)
{
}
};
template <typename C>
struct TestHelper<C, 0>
{
static void check (C & a)
{
}
};
template <int A, int B, typename C>
class Test
{
template <int V> void check(C & a)
{
TestHelper<C, V>::check (a);
}
};
}