When function templates are instantiated in a class template? - c++

At which moment the function templates within the following class template are instantiated?
// a.h
#pragma once
template <typename T>
class A {
public:
template <typename T2> void func1(T2 t);
void func2(T t);
T func3();
void func4();
// SECONDARY ISSUE
// Is there any difference in writing this:
A& operator=(A const&) = default;
// or this:
A<T>& operator=(A<T> const&) = default;
};
-----------------------------
// a.cpp
#include "a.h"
template <typename T>
template <typename T2>
void A<T>::func1(T2 t)
{
// do sth
}
template <typename T>
void A<T>::func2(T t)
{
// do sth
}
template <typename T>
T A<T>::func3()
{
T t;
// do sth
return t;
}
template <typename T>
void A<T>::func4()
{
T t;
// do sth with t
}
template class A<int>; // explicit instantiation
-----------------------------
// main.cpp
#include "a.h"
int main()
{
A<int> a1;
a1.func1<double>(1.);
a1.func1(1.);
a1.func2(2);
a1.func3();
a1.func4();
}
In a free function template the template is instantiated when it's called with a concrete type or with explicit instantiation.
What's the case with class templates? I guess func2() - func4() are instantiated with the explicit class template instantiation template class A<int>;. Or takes the instantiation place at the moment of the first function call, i.e. for example a1.func2(2.)?
In case of func1() the instantiation takes probably place with the call of a1.func1<double>(1.); as this is the first time when the second template parameter T2 is known?
Concerning the secondary issue:
Does it matter if I write A or A<T>? I think it's the same but I am not sure about it.

Methods of class templates are only instantiated when called. For illustration consider:
#include <type_traits>
template <typename T>
struct foo {
void only_for_int() {
static_assert(std::is_same<T,int>::value);
}
};
int main(){
foo<int> f;
f.only_for_int(); // OK (T == int)
foo<double> d; // OK
//d.only_for_int(); // ERROR: static assert failed
}
Creating a foo<double> is fine. You just cannot call its method only_for_int.
The fact that not every method must be valid is quite handy to allow types that cannot support all the templates requirements to use at least a subset of the templates methods.

Related

Code compiles on one machine, but not on another [duplicate]

Is it legal to use an incomplete type in a template if the type is complete when the template is instantiated?
As below
#include <iostream>
struct bar;
template <typename T>
struct foo {
foo(bar* b) : b(b) {
}
void frobnicate() {
b->frobnicate();
}
T val_;
bar* b;
};
struct bar {
void frobnicate() {
std::cout << "foo\n";
}
};
int main() {
bar b;
foo<int> f(&b);
f.frobnicate();
return 0;
}
Visual Studio compiles the above without complaining. GCC issues the warning invalid use of incomplete type 'struct bar' but compiles. Clang errors out with member access into incomplete type 'bar'.
The code is ill-formed, no diagnostic required.
[temp.res.general]/6.4
The validity of a template may be checked prior to any instantiation.
The program is ill-formed, no diagnostic required, if:
...
— a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, ...
If you absolutely can't define bar before the template, there is a workaround: you can introduce an artifical dependency on the template parameter.
template <typename T, typename, typename...>
struct dependent_type {using type = T;};
template <typename T, typename P0, typename ...P>
using dependent_type_t = typename dependent_type<T, P0, P...>::type;
Then use dependent_type_t<bar, T> instead of bar.
Clang is correct in reporting an error (as opposed to a warning or being silent about it), though MSVC's and GCC's behavior are also consistent with the standard. See #HolyBlackCat's answer for details on that.
The code you posted is ill-formed NDR. However, what you want to do is feasible.
You can defer the definition of template member functions the same way you would for a non-template class. Much like non-template classes, as long as these definitions requiring bar to be a complete type happen only once bar is complete, everything is fine.
The only hiccup is that you need to explicitly mark the method as inline to avoid ODR violations in multi-TU programs, since the definition will almost certainly be in a header.
#include <iostream>
struct bar;
template <typename T>
struct foo {
foo(bar* b) : b(b) {
}
inline void frobnicate();
T val_;
bar* b;
};
struct bar {
void frobnicate() {
std::cout << "foo\n";
}
};
template <typename T>
void foo<T>::frobnicate() {
b->frobnicate();
}
int main() {
bar b;
foo<int> f(&b);
f.frobnicate();
return 0;
}
If you want to customise a template using a forward declaration as a temlpate argument, you can do this (wihtout warnings or errors):
template <typename T, typename = T>
class print_name;
so when you do a partial specialization, you use the second, unspecialized template parameter for your calls:
struct john;
template <typename T>
class print_name<john, T>
{
public:
void operator()(const T& f) const
{
std::cout << f.name << std::endl;
}
};
In this context T is not incomplete. But when you instantiate print_name<john>, SFINAE will kick.
Here is a full example:
#include <iostream>
template <typename T, typename = T>
class print_name;
struct john;
template <typename T>
class print_name<john, T>
{
public:
void operator()(const T& f) const
{
std::cout << f.name << std::endl;
}
};
struct slim;
template <typename T>
class print_name<slim, T>
{
public:
void operator()(const T& f) const
{
std::cout << f.myName << std::endl;
}
};
#include <string>
struct john
{
std::string name;
};
struct slim
{
std::string myName;
};
int main()
{
print_name<john>{}(john{"John Cena"});
print_name<slim>{}(slim{"Slim Shady"});
return 0;
}
https://godbolt.org/z/czcGo5aaG

Constrain member function template in class template with inner type same as class template type

I want a member function that more or less does the same thing as this:
template<class T, template<class T> class U>
void f(const U<T>& x)
{
...
}
...but in a template class already parameterized on T:
template<class T>
class A
{
???
// maybe template< template<class T> class U>
// maybe template< class U<T> >
void f(const U<T>& x)
{
...
}
}
This might be doable using type aliases (with using), but this is an older C++98 codebase, so I can't try that route.
You can simply use T inside the member function template when deducing the argument:
template<class T>
struct A
{
template<template<class> class U>
void f(const U<T> & x) {}
};
This constrains the function template f to only accept arguments that are instantiations of a class on T.
A<int> a;
S<int> s1;
a.f(s1); // ok, a and s1 are instantiated over int
a.f(42); // error, 42 is not a U<T>
S<double> s2;
a.f(s2); // error, a is instantiated over int,
// but s2 is instantiated over double
Here's a demo.

template class method specialization using concepts

There is a template class A with template parameter T. I want this class to have a method f if T is of integral types. The class A also has a lot of other methods, so I don't want to have specialization of overall A.
I understand that this problem can be solved using inheritance, but my question is about concepts and requirements.
This code
template <typename T>
struct A {
void f();
};
template <>
void A<int>::f() {}
works as I expect. It makes implementation of f for the int type only. If I try to call A<std::string>{}.f(); it generates a linker error as expected.
But if I write
template <typename T>
struct A {
void f();
};
template <std::integral T>
void A<T>::f() {}
either
template <typename T> requires std::is_integral_v<T>
void A<T>::f() {}
the method f is generated for all types, so calling A<std::string>{}.f(); does not give any error.
Also this works
template <typename T>
struct A {
void f() {}
};
template <>
void A<std::string>::f() = delete;
but this
template <typename T>
struct A {
void f() {}
};
template <std::integral T>
struct A<T>::f() = delete;
gives compilation error, namely redefinition of f.
P.S. It seems such constructions are not allowed at all, but g++ just ignores concepts in definition of method f.
There are four syntactical methods of applying constraints to a function.
Type constraint in a template parameter list; template< Concept TypeID >.
Requires clause after a template parameter list; template< class TypeID > requires constexpr-andor-requires-expression.
Constraint on auto in an abbriviated function template; void f(Concept auto id);.
Requires clause after a function declaration; template< class TypeID > void f() requires constexpr-andor-requires-expression.
The function you want to constrain doesn't have a template parameter list so you can't use methods 1 and 2. Method 3 essentially generates template parameters. So that leaves method 4.
#include <concepts>
template< class T >
struct A {
void f() { /* do something */ }
void g() requires std::integral<T> { /* do something */ }
void h() requires std::integral<T>;
template< std::integral U = T >
void i() { /* do something */ }
};
template< class T >
void A<T>::h() requires std::integral<T> { /* do something */ }
int main() {
A<double> dblA;
dblA.f();
// dblA.g(); // A<double>::g() is not declared or defined
// dblA.h(); // A<double>::h() is not declared or defined
// dblA.i(); // A<double>::h<double>() is not declared or defined
dblA.i<int>(); // A<double>::h<int>() is declared and defined
A<int> intA;
intA.f();
intA.g(); // A<int>::g() is declared and defined
intA.h(); // A<int>::h() is declared and defined
intA.i(); // A<int>::h<int>() is declared and defined
//intA.i<double>(); // A<int>::h<double>() is not declared or defined
return 0;
}
You have to add a new template parameter, which will default to T and to which you can add your constraint of std::integral<>. This compiled with gcc10.3.0 and clang12.0.0, other versions you will have to test yourself.
The code:
#include <concepts>
#include <string>
template <typename T>
struct A
{
template <typename U = T>
requires std::integral<U>
void f();
};
template <typename T>
template <typename U>
requires std::integral<U>
void A<T>::f()
{
}
int main()
{
A<int> a;
a.f();
A<std::string> s; // This works
// s.f(); // Compilation error: constraint not satisfied
return 0;
}

Function specialized with template

I need to make a specialization of my function with template class and have problem with "illegal use of explicit template arguments".
template <typename T>
class MyClass { /* ... */ }; // it can be any template class, eg std::vector
template <typename T>
void foo() { /* ... */ } // my template function which need a specialization
template<>
void foo<int>() /* sth special for integers - it works */ }
template<template T>
void foo<MyClass<T> >() /* sth special for template class with any parameter - it doesnt work :( */ }
Of course i can type a few specialization for all MyClass'es which i need to, but maybe it can be replaced with one?
Template specialization of function is not as flexible as specialization of struct: only full specialization is allowed. If you want to do partial specialization you need to wrap your foo function inside a struct:
template <typename T> class MyClass { };
template <typename T> struct Foo;
template <typename T> struct Foo { void foo() {}};
template<> struct Foo<int> { void foo() { } };
template<typename T> struct Foo< MyClass<T> > { void foo() {} };
And then instead of calling
foo<MyClass<...>>()
you call
Foo< MyClass<...> >::foo()
You cannot partially speciallise a template function. There are discussions about removing that restriction though.
The advocated workarounds are:
Use a class template from the template function.
Wrap your function in a template class.
template <typename T>
struct foo_impl {
};
template <typename T>
void foo() {
foo_impl<T>();
}
// And now specialize foo_impl as you want:
template<>
struct foo_impl<int> {
foo_impl(){/* sth special for integers - it works */}
};
template<typename T>
struct foo_impl<myclass<T>> {
foo_impl() {/* ... */}
};
If you wanted a return-value, you should use a member-function - probably operator() - instead of the ctor.
This is a lot of extra typing, but how about:
template <typename T>
class MyClass { /* ... */ }; // it can be any template class, eg std::vector
template<typename T>
struct FooWrapper
{
static void foo()
{
// default implementation
}
};
template<typename T>
struct FooWrapper<MyClass<T>>
{
static void foo()
{
// MyClass<T> implementation
}
};
template<typename T>
void foo()
{
FooWrapper<T>::foo();
}
A possible solution could be using a base class
template<typename T> class MyClass;
class base {
private:
template<typename T> friend class MyClass;
base(); // Can't build a base object directly
};
template <typename T>
class MyClass : public base {
public:
}; // it can be any template class, eg std::vector
template <typename T>
void foo() {
} // my template function which need a specialization
template<>
void foo<int>() { /* sth special for integers - it works */ }
template<>
void foo<base>() { /* sth special for template class with any parameter - it doesnt work :( */ }
The above might also work in case you want a template parameter to your function. If you can wrap your function up I'd go with hivert's solution.

Double template<> statements

There is such function definition:
template<>
template<>
void object::test<1>()
{
}
What does it mean that there are double template<>?
EDIT:
I extracted code which is valid for this example:
#include <iostream>
template <class U>
class A {
template <int n>
void test() {
}
};
template <class T>
class B {
public:
typedef A<T> object;
};
typedef B<int>::object object;
template<>
template<>
void object::test < 1 > () {
}
int main() {
return 0;
}
This code compiles under g++.
Source: TUT test framework
For example,
template<class T = int> // Default T is int
class object<T> {
template<int R> void test ();
};
Your code:
template<> // T is fixed
template<> // R is fixed
void object<>::test<1>() // T is default int, R is 1
{
}
is equivalent to:
template<> // T is fixed
template<> // R is fixed
void object<int>::test<1>() // T is int, R is 1
{
}
This is the template specialization of a class template member function template (did I get that right?), with a default parameter for the template. Look at this:
template<typename T = int>
struct X {
template<typename U>
void foo(U u);
};
template<>
template<>
void X::foo(float f) { }
This introduces a specialization for the case where the template parameter of X is int and the argument to X<int>::foo is float. This case is slightly different from yours, we don't have to provide the template argument explicitly after the name of the member function as it can be deduced. Your function has a non-type template argument which cannot be deduced and as such must be provided.
What confused me the most is the default template argument and I'm unsure if it is good practice to use skip it. I'd provide it for every specialization to avoid confusion.
That looks to me like a specialization of a function template within a class template. For example, consider the following class template definition:
template <int m=1>
class object
{
public:
template <int n>
void test();
};
// This differs from your example, by the addition of `<>` after `object`, but it's as
// close as I can come to valid code true to your example
template<>
template<>
void object<>::test<1>()
{
}