I want to specialize a member function of a class template as follows:
#include <concepts>
template <typename T>
struct S {
void f();
};
template <typename T>
void S<T>::f() {
}
// (0) This is fine.
template <>
void S<int>::f() {
}
// (1) This triggers an error.
template <std::integral T>
void S<T>::f() {
}
The specialization (0) is fine, but specializes f() only for the int type. Instead, I would like to specialize it, e.g., for any integral type, as in (1). Is this possible using C++20 concepts? Notice that std::integral is just an example and that my specific case makes use of user-defined concepts.
Simply use a trailing requires-clause. The compiler will choose the most constrained function:
#include <concepts>
#include <iostream>
template <typename T>
struct S {
void f();
void f() requires std::integral<T>;
};
template <typename T>
void S<T>::f() {
std::cout << "general\n";
}
template <typename T>
void S<T>::f() requires std::integral<T> {
std::cout << "constrained\n";
};
int main() {
S<int> x;
S<double> y;
x.f(); // prints constrained
y.f(); // prints general
return 0;
}
Related
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
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;
}
By using template template parameters one can pass to a class a templated class without specifying types on its parameters. I was wondering is there a way to pass into a template template parameter a templated signature of a function to be able to specialize which variant of the function is to be considered forward.
To be clear - I know I cannot do that:
template <class T>
void foo() { /*...*/ }
template <template <class...> class FooType>
struct Foo { /*...*/ };
int main() {
Foo<decltype(foo)> f;
}
But somehow I would like to be able to pass templated signature of function to Foo. Is it even possible?
I couldn't believe that this is not possible so I searched a bit and found a way to do exactly what I wanted. I used templated using with a syntax:
template <template<class... Args> class FooType>
struct Foo {
FooType<int> ft;
};
template <class Res, class... Args>
using FooSignature = Res(*)(Args...);
int foo() {
return 1;
}
int main() {
Foo<FooSignature> f;
f.ft = foo;
}
This however still leaves the question how can this be possible since the standard states something opposite.
In the example below one has a template template parameter that accepts the preferred signature for the function.
Because of the specialization and the lack of a body for the template class, only types for callables are accepted.
It is a generalization of what the OP actually asked:
#include<cassert>
template<typename F>
struct S;
template<typename R, typename... Args>
struct S<R(Args...)> {
using type = R(*)(Args...);
};
template<template<typename> class F>
struct T {
typename F<void(int)>::type ft;
typename F<double(double, double)>::type gt;
};
void f(int) { }
double g(double x, double y) { return x+y; }
int main() {
T<S> t;
t.ft = f;
t.gt = g;
t.ft(42);
auto v = t.gt(1., 1.);
assert(v == 2.);
}
As can be seen in this answer
Template of function pointer is illegal in C++
The C++ Standard says in $14/1,
A template defines a family of classes or functions.
Further quoting from the linked answer:
Please note that it does NOT say "A template defines a family of classes, functions or function pointers"
However, you can pass concrete function pointers, and specialise on their signature:
#include <iostream>
template <class T>
void foo(T) { }
template <typename>
struct Foo;
template<typename T>
struct Foo<void(T)>
{
void cb() { std::cout << "T\n"; }
};
template<>
struct Foo<void(int)>
{
void cb() { std::cout << "int\n"; }
};
template<>
struct Foo<void(double)>
{
void cb() { std::cout << "double\n"; }
};
int main()
{
Foo<decltype(foo<int >)>().cb(); // outputs 'int'
Foo<decltype(foo<double>)>().cb(); // outputs 'double'
Foo<decltype(foo<char >)>().cb(); // outputs 'T'
return 0;
}
template of template is still a template.
template <class T>
void foo() { /*...*/ }
template <typename T>
struct Foo { /*...*/ };
int main() {
Foo<decltype(foo<int>)> f;
}
You cannot pass a function template as an argument. What you can do is wrap a function template in a generate lambda taking a tag parameter:
template <class T> struct tag_t { using type = T; };
template <class T>
void foo() { ... }
template <class F>
void call_func_with(F f) {
f(tag_t<int>{} );
f(tag_t<double>{} );
}
call_with_func([](auto tag) { foo<decltype(tag)::type>(); } );
Here, f(tag_t<X>{} ) ends up calling foo<X>(), as desired.
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>()
{
}
Sometimes it works sometimes not:
template <class T>
void f(T t) {}
template <class T>
class MyClass {
public:
MyClass(T t) {}
};
void test () {
f<int>(5);
MyClass<int> mc(5);
f(5);
MyClass mc(5); // this doesn't work
}
Is there a way to hack around the example above? I.e. force the compiler to infer the template parameter from constructor parameter.
Will this be fixed in the future, or is there a good reason not to?
What is the general rule when compiler can infer template parameter?
Template parameters can be inferred for function templates when the parameter type can be deduced from the template parameters
So it can be inferred here:
template <typename T>
void f(T t);
template <typename T>
void f(std::vector<T> v);
but not here:
template <typename T>
T f() {
return T();
}
And not in class templates.
So the usual solution to your problem is to create a wrapper function, similar to the standard library function std::make_pair:
template <class T>
class MyClass {
public:
MyClass(T t) {}
void print(){
std::cout<<"try MyClass"<<std::endl;
}
};
template <typename T>
MyClass<T> MakeMyClass(T t) { return MyClass<T>(t); }
and then call auto a = MakeMyClass(5); to instantiate the class.
Read up on Template Argument Deduction (and ADL or Koenig lookup).
Since C++20, it is possible to infer the types of function parameters using auto:
#include <iostream>
#include <string>
auto print_stuff(auto x, auto y)
{
std::cout << x << std::endl;
std::cout << y << std::endl;
}
int main()
{
print_stuff(3,"Hello!");
print_stuff("Hello!",4);
return 0;
}