I read some complex template class design, it looks like recirsive template deduction with template parameter refers to the template itself.
I simplified the codes as follow, does anybody can help explain how the template deduction works for the follow codes.
also I want to know more knowledge about the programming technique used in this demo and can you share me any c++ standard material about this topic.
#include <iostream>
template<typename X = char, class T = int>
class ABC;
template<class X, class T>
class ABC <X, ABC<X, T>>{
public:
T t;
};
template<class T, class M>
class ABC : public ABC<T, ABC<T,M>> {};
int main() {
ABC<int, char> abc;
abc.t = 1;
ABC<> abcd;
abcd.t = 3;
std::cout << abc.t << " " << abcd.t << std::endl;
}
as shown above, I tried simplified the code and write a little demo.
my compiler is gcc 9.4.0 released with ubuntu20, with compile args -std=c++17.
What I concerned most is how abc or abcd is deduced as ABC<T, M> is inherited from ABC<T, ABC<T, M>>, and ABC<T, M> seems is a self-referencing template class?
And also how the template deduction terminated as it seems there is only a template class declaration template<typename X = char, class T = int> class ABC; and seems no implemention of it.
abc.t is print as unvisable char succeeding with a whitespace and then a charater 3, as below.
3
Related
According to my understanding, the following two programs should be effectively identical, but the second fails to compile. Note that in this simplified example, the nesting is obviously useless, but it's necessary in the original context.
Version 1: works as expected
#include <iostream>
template<typename Value>
struct Anything {
Value value;
};
using A = const char *;
template<template<typename> class B>
struct Inner {
template<typename C>
using type = B<C>;
};
template<template<typename> class B>
struct OuterBase {
B<A> b;
void
print() { std::cout << "b: " << b.value << std::endl; }
};
using Outer = OuterBase<Inner<Anything>::type>; // this version works
int
main()
{
Outer outer;
outer.b.value = "foo";
outer.print();
return 0;
}
The compiler error in this version is "expected a template, got TypeBox<A>::Inner<Anything>::type".
Version 2: fails with compile error
#include <iostream>
template<typename Value>
struct Anything {
Value value;
};
template<typename A>
struct TypeBox {
template<template<typename> class B>
struct Inner {
template<typename C>
using type = B<C>;
};
template<template<typename> class B>
struct OuterBase {
B<A> b;
void
print() { std::cout << "b: " << b.value << std::endl; }
};
using Outer = OuterBase<Inner<Anything>::type>; // compile error here
};
int
main()
{
using Box = typename TypeBox<const char *>::Outer;
Box box;
box.b.value = "foo";
box.print();
return 0;
}
I would expect the TypeBox struct to be transparent when resolving the types inside, but according to gcc 12.2.1, it isn't. Why not?
The difference in your second example is that a compiler cannot verify at the point of definition of the template that Inner<Anything>::type is really a class template or alias template. The problem is that Inner is a nested class template and you could in theory partially or explicitly specialize Inner for the given template argument later, so that type names e.g. a type.
So compilers tend to require that you place the template keyword before the template's name in such a case to state that it really is a template:
using Outer = OuterBase<Inner<Anything>::template type>;
My understanding is that technically there is nothing in the standard requiring this use of template though, but that this wasn't entirely clear. However, the standard did allow using the template keyword in this position, but will deprecate this allowance with C++23. If I am not mistaken compilers should also allow this without template keyword in the future even for previous standard revisions (as defect report). There wasn't ever really a point in performing this test.
See also CWG issue 1478 and the paper proposing the deprecation P1787R6.
I have the following problem( see code below).
In a class that inherits from a base class, I want to be able to have two implementations of operator(). One that takes integers, one that takes "indices", see simple Index class.
The operator(ints...) will be in the child class, while I need to have the operator(Index...) in the parent class (also with different return type.)
The code below is not the exact design but a minimal working example explaining the final problem.
Problem is, if I put operator(Index...) in the child class, everything is good. If I put it in the base class, i get a compilation error:
error: no match for call to ‘(Tensor<1, int>) (Index<'i'>&)’
when I call vec(i) and the end of the main(). I understand the compiler doesn't find the good method, but why ? Is there some "shadowing" rule associated to variadic templates ?
Thanks !
#include <iostream>
#include <type_traits>
template<char i>
class Index{
public:
Index(){};
};
template<int order, typename T, class tensor_type>
class Tensor_traits{
public:
//// here, doesn't compile ! !
//template <char i>
//T&operator()(Index<i> &ii){
//std::cout << "puet" << std::endl;
//}
};
template<int order, typename T>
class Tensor : public Tensor_traits<order, T, Tensor<order, T>> {
private:
int data[3] = {1,2,3};
public:
Tensor(){};
template <typename... Idx>
typename std::enable_if<std::is_same<Idx...,int>::value
or std::is_same<Idx...,unsigned int>::value
or std::is_same<Idx...,size_t>::value, T&>::type
operator()(const Idx&... idx){
return data[0]; //dummy here, actually i do other stuff !
}
// here, works!
template <char i>
T&operator()(Index<i> &ii){
std::cout << "puet" << std::endl;
}
};
int main() {
Tensor<1,int> vec1;
std::cout<< vec1(1) << std::endl;;
Index<'i'> i;
vec1(i);
}
Try adding
using Tensor_traits<order, T, Tensor<order, T>>::operator();
or (suggested by Yakk (thanks) to have Base for other uses)
using Base = Tensor_traits<order, T, Tensor<order, T>>;
using Base::operator();
in Tensor
The problem isn't that the base class is a template class; the problem is that the derived class define another operator() method function that hide the operator() method inherited.
To verify this, delete the operator() defined in the derived class and you can see that, also without using, you can use the operator() of the base class.
Using using unhide the operator() of the base class, when an operator() is defined in the derived class, so you can use both.
In the following code, what is the advantage of using &&?
The code is from answer at Specialize same operator for different traits
From this question, I get that an && argument means it is a reference that can be modified by the function.
The decay_t probably prevents the compiler to interpret a reference to a variable as an array, as in What is std::decay and when it should be used?
std::forward is perfect forwarding as described here. Why do we need this forwarding?
Thanks.
#include <iostream>
#include <type_traits>
#include<utility>
class A;
template <typename T>
struct is_A : std::false_type {};
template <> struct is_A<A> : std::true_type {};
template <typename T>
struct is_int : std::false_type {};
template <> struct is_int<int> : std::true_type {};
template <> struct is_int<long> : std::true_type {};
class A{
public:
int val;
void print(void){
std::cout << val << std::endl;
}
template <typename T1>
std::enable_if_t<is_int<std::decay_t<T1>>::value, void>
operator=(T1 && input){
val = 2*std::forward<T1>(input);
}
template <typename T1>
std::enable_if_t<is_A<std::decay_t<T1>>::value,void>
operator=(T1 && Bb){
val = 5*std::forward<T1>(Bb).val;
}
};
int main(void){
A Aa;
A Bb;
int in_a = 3;
Aa = in_a;
Bb = Aa;
Aa.print(); //This should give 6. (3x2)
Bb.print(); //This should give 30. (6x5)
}
Actually, it's a (let me say) trick so as the example code works.
In fact, the standard says that:
A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X, X&, const X&, volatile X& or const volatileX&.
Moreover:
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly.
Now, try to copy-assign a const reference to the variable, as an example:
Bb = static_cast<const A&>(Aa);
The result will be no longer the one expected.
Anyway, as long as you deal only with non cv-qualified lvalue/rvalue references, the forwarding reference used in the answer works fine, as shown in the example code.
It intercepts a bunch of specific types and does its work. That's all.
As mentioned in the comments by #Jarod42:
Note that you still have to write the operator=(const A&) to handle it as it is special.
Not so special indeed. You don't have a proper copy assignment operator in that code if you don't define it.
As said in the comments, to define it
[...] is left as an exercise to the reader. :-)
So, what's the advantage of doing that?
A shortcut to offer a minimal, working solution. Not a complete, production-ready piece of code, of course.
I went into trouble understanding the following codes:
#include <iostream>
using namespace std;
template <class PixType, bool B = PixType::is>
class Test {
public:
void print() {
cout << "PixType B=false" <<endl;
}
};
template <class PixType>
class Test<PixType,true> {
public:
void print() {
cout << "PixType B=true" <<endl;
}
};
class PT {
public:
static const bool is = false; // here is the magic
};
int main() {
Test<PT> t;
t.print();
}
I have 4 questions:
What's the difference between the first template test class(with <> right after template instead of the class name) and the second one(with <> after class name Test).
Why can two same classes(both are named Test) exist at the same time.
In fact, when the is in class PT is set to be true, the program will print out PixType B=true, otherwise PixType B=false, and I don't know the magic here.
I tried to change the second template Test class to be like:
template <class PixType, bool B= true>
class Test {
public:
void print() {
cout << "PixType B=true" <<endl;
}
};
But the compiler will warn me of name conflict.
I am really new to these template stuff, and got really confused by these lines of codes. And I don't know how to search for this trick. In most articles about C++ templating, they just talk about some basic ideas and usages about template.
Any help will be greatly appreciated.
Point 1 What's the difference between the first template test class(with <> right after template instead of the class name) and the second one(with <> after class name Test).
Response The first one is the main class template definition. The second one is a specialization of the class template where the second parameter of the template is true.
If you didn't have the second one, the first one will be used regardless of whether the second parameter of the template is true or false.
Point 2 Why can two same classes(both are named Test) exist at the same time.
Response In your case, the second one is a specialization of the first one. When you use:
Test<int, true> a;
The compiler knows to use the specialization to generate code for Test<int, true>. When you use:
Test<int, false> b;
The compiler know to use the first one to generate code for Test<int, false>.
Point 3 In fact, when the is in class PT is set to be true, the program will print out PixType B=true, otherwise PixType B=false, and I don't know the magic here.
Response As I explained above, when the second parameter is true, the compiler uses the second class template. Otherwise, it uses the first class template. I hope there is no confusion about that any more.
Point 4 I tried to change the second template Test class to be like:
template <class PixType, bool B= true>
class Test {
public:
void print() {
cout << "PixType B=true" <<endl;
}
};
But the compiler will warn me of name conflict.
Response
Class templates can only be specialized, they cannot be overloaded.
template <typename A, typename B> class C1 {};
template <typename A> class C1 {};
The above is not supported by the language.
template <typename A, typename B> class C1 {};
template <typename A> class C1<A, A*> {};
The above is supported by the language. The second class template is a specialization of the first class template.
What you are trying to do is provide a default value for the second parameter but the name used to define the class template is same. The second one is a different class template not a specialization of the first class template.
I have this simple code:
class A{};
class B : public A{};
class C : public B{};
class Test
{
public:
template<typename T>
void f(T&){printf("template\n");}
void f(A&){printf("specialization\n");}
};
int main()
{
A a;
B b;
C c;
Test test;
test.f(a);
test.f(b);
test.f(c);
}
When I run it(VS2010) I have this output:
specialization
template
template
Is it possible to make the calls with A-derived classes to use specialization?
Yes, it is possible, but you have to change your code a bit.
First of all, to be technical, the second function f() is not a specialization of the template function, but an overload. When resolving overload, the template version is chosen for all arguments whose type is not A, because it is a perfect match: T is deduced to be equal to the type of the argument, so when calling f(b), for instance, after type deduction the compiler will have to choose between the following two overloads:
void f(B&){printf("template\n");}
void f(A&){printf("specialization\n");}
Of course, the first one is a better match.
Now if you want the second version to be selected when the function is invoked with an argument which is a subclass of A, you have to use some SFINAE technique to prevent the function template from being correctly instantiated when the type T is deduced to be a subclass of A.
You can use std::enable_if in combination with the std::is_base_of type traits to achieve that.
// This will get instantiated only for those T which are not derived from A
template<typename T,
typename enable_if<
!is_base_of<A, T>::value
>::type* = nullptr
>
void f(T&) { cout << "template" << endl; }
Here is how you would use it in a complete program:
#include <type_traits>
#include <iostream>
using namespace std;
class A{};
class B : public A{};
class C : public B{};
class D {};
class Test
{
public:
template<typename T,
typename enable_if<!is_base_of<A, T>::value>::type* = nullptr
>
void f(T&) { cout << ("template\n"); }
void f(A&){ cout << ("non-template\n");}
};
int main()
{
A a;
B b;
C c;
D d;
float f;
Test test;
test.f(a); // Will print "non-template"
test.f(b); // Will print "non-template"
test.f(c); // Will print "non-template"
test.f(d); // Will print "template"
test.f(f); // Will print "template"
}
EDIT:
If you are working with a compiler which is not fully compliant with C++11 (and therefore does not support default template arguments on function templates), you might want to change the definition of your template overload of f() as follows:
template<typename T>
typename enable_if<!is_base_of<A, T>::value, void>::type
f(T&) { cout << ("template\n"); }
The behavior of the program will be identical. Note that if the return type of f() is void, you can omit the second argument to the enable_if class template.