template class method specialization using concepts - c++

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;
}

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

When function templates are instantiated in a class template?

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.

c++ template specialization with open template arguments [duplicate]

The following code:
template <typename S, typename T>
struct foo {
void bar();
};
template <typename T>
void foo <int, T>::bar() {
}
gives me the error
invalid use of incomplete type 'struct foo<int, T>'
declaration of 'struct foo<int, T>'
(I'm using gcc.) Is my syntax for partial specialization wrong? Note that if I remove the second argument:
template <typename S>
struct foo {
void bar();
};
template <>
void foo <int>::bar() {
}
then it compiles correctly.
You can't partially specialize a function. If you wish to do so on a member function, you must partially specialize the entire template (yes, it's irritating). On a large templated class, to partially specialize a function, you would need a workaround. Perhaps a templated member struct (e.g. template <typename U = T> struct Nested) would work. Or else you can try deriving from another template that partially specializes (works if you use the this->member notation, otherwise you will encounter compiler errors).
Although coppro mentioned two solutions already and Anonymous explained the second one, it took me quite some time to understand the first one. Maybe the following code is helpful for someone stumbling across this site, which still ranks high in google, like me. The example (passing a vector/array/single element of numericalT as dataT and then accessing it via [] or directly) is of course somewhat contrived, but should illustrate how you actually can come very close to partially specializing a member function by wrapping it in a partially specialized class.
/* The following circumvents the impossible partial specialization of
a member function
actualClass<dataT,numericalT,1>::access
as well as the non-nonsensical full specialisation of the possibly
very big actualClass. */
//helper:
template <typename dataT, typename numericalT, unsigned int dataDim>
class specialised{
public:
numericalT& access(dataT& x, const unsigned int index){return x[index];}
};
//partial specialisation:
template <typename dataT, typename numericalT>
class specialised<dataT,numericalT,1>{
public:
numericalT& access(dataT& x, const unsigned int index){return x;}
};
//your actual class:
template <typename dataT, typename numericalT, unsigned int dataDim>
class actualClass{
private:
dataT x;
specialised<dataT,numericalT,dataDim> accessor;
public:
//... for(int i=0;i<dataDim;++i) ...accessor.access(x,i) ...
};
If you need to partially specialise a constructor, you might try something like:
template <class T, int N>
struct thingBase
{
//Data members and other stuff.
};
template <class T, int N> struct thing : thingBase<T, N> {};
template <class T> struct thing<T, 42> : thingBase<T, 42>
{
thing(T * param1, wchar_t * param2)
{
//Special construction if N equals 42.
}
};
Note: this was anonymised from something I'm working on. You can also use this when you have a template class with lots and lots of members and you just want to add a function.
If you're reading this question then you might like to be reminded that although you can't partially specialise methods you can add a non-templated overload, which will be called in preference to the templated function. i.e.
struct A
{
template<typename T>
bool foo(T arg) { return true; }
bool foo(int arg) { return false; }
void bar()
{
bool test = foo(7); // Returns false
}
};
In C++ 17, I use "if constexpr" to avoid specialize (and rewrite) my method. For example :
template <size_t TSize>
struct A
{
void recursiveMethod();
};
template <size_t TSize>
void A<TSize>::recursiveMethod()
{
if constexpr (TSize == 1)
{
//[...] imple without subA
}
else
{
A<TSize - 1> subA;
//[...] imple
}
}
That avoid to specialize A<1>::recursiveMethod().
You can also use this method for type like this example :
template <typename T>
struct A
{
void foo();
};
template <typename T>
void A<T>::foo()
{
if constexpr (std::is_arithmetic_v<T>)
{
std::cout << "arithmetic" << std::endl;
}
else
{
std::cout << "other" << std::endl;
}
}
int main()
{
A<char*> a;
a.foo();
A<int> b;
b.foo();
}
output :
other
arithmetic

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>()
{
}

Explicit template specialization issue

template <class T>
class Test
{
public:
template<class U> void f(); //generic function
template<> void f<char>(); //Specialization for char.
};
template <class T>
template<class U>
void Test<T>::f() //Definition of generic function
{
}
template<>
template<> void Test<char>::f<char>(){} //Definition of specialization.
int main()
{
Test<char> ob1;
ob1.f<char>(); //Works fine.
Test<int> ob2;
ob2.f<char>(); //Produces linker error.
}
Linker error is
error LNK2019: unresolved external symbol "public: void __thiscall
Test<int>::f<char>(void)"
My requirement is: I should be able to pass any type to Test class and any type to function f(). I should be able to use all combinations of types like below.
Test f()
--------------
int char
char int
int int
I can solve the error by defining another function like below.
template<>
template<> void Test<int>::f<char>(){}
But then what is the use of making Test class as Template ? How to make it work for all combinations ?
C++03, §14.7.3/2:
An explicit specialization shall be declared in the namespace of which the
template is a member, or, for member templates, in the namespace of which
the enclosing class or enclosing class template is a member.
An explicit specialization of a member function, member class or static data
member of a class template shall be declared in the namespace of which the
class template is a member.
Therefore you should declare your specialization outside of a class, for example:
template <class T>
class Test
{
public:
template<class U> void f(); //generic function
};
template <class T>
template <class U>
void Test<T>::f() {} //Definition of generic function
template<>
template<>
void Test<char>::f<char>(){} //Specialization.
int main()
{
Test<char> ob1;
ob1.f<char>();
Test<int> ob2;
ob2.f<char>();
}
The problem that you are facing is that you have declared the specialization of f for char in the Test template, and that is incorrect. The compiler is not detecting the error, but it is getting confused and interpreting that you want to provide the specialization of f for char in all template instantiations:
template <typename T>
struct Test {
template <typename U> void f();
template <> void f<char>(); // <- Incorrect
};
When you write Test<int> the compiler instantiates the template and is (mistakenly) accepting it and interepreting that there is an specialization of f for char in Test<int>.
Just remove the line, and you will get the code to compile. It will use the specialization only for Test<char>::f<char>(), and I am not sure whether that is what you want.
If your intention is specializing f for char with all instantiating types, that is not allowed. When you define a template specialization, all enclosing templates be specialized. A common work around is not providing an specialization but a different overload of the member function:
template <typename T>
struct Test {
template <typename U> void f( U );
void f( char );
};
But that won't help you much there, as you cannot provide different overloads for the same arguments (in your case no arguments). Also, in your case you must explicitly call the template to differentiate, and code that explicitly requests the template would not pick up the overload:
int main() {
Test<int> t;
t.f<char>(); // will call template, not "void f(char)"!!
}
Without more details on what you really want to achieve I cannot think on other potential solution to the issue.
My requirement is: I should be able to pass any type to Test class and any type to function f(). I should be able to use all combinations of types like below.
Why do you need an explicit specialization? Why do you unnecessarily want to make your code complex?
The following works for all combinations that you have listed.
template <class T>
class Test
{
public:
template<class U> void f();
};
template <class T>
template<class U>
void Test<T>::f(){}
int main()
{
Test<char> ob1;
ob1.f<char>(); //Works fine. T = char, U = char
Test<int> ob2;
ob2.f<char>(); //Works fine T = int, U = char
}