I have the following code which is not compiled with gcc, but compiles perfectly with MSVC.
class B;
class A
{
B *_parent;
public:
template <typename T>
void Do(T val)
{
_parent->DoB(val);
}
A(B *parent)
: _parent(parent)
{
}
};
class B : public A
{
public:
B()
: A(nullptr)
{
}
void DoB(int val)
{
cout << val << endl;
}
};
int main() {
B b;
A a(&b);
a.Do(10);
return 0;
}
Compilation error is as follows:
prog.cpp: In member function ‘void A::Do(T)’:
prog.cpp:15:10: error: invalid use of incomplete type ‘class B’
_parent->DoB(val);
^~
prog.cpp:4:7: note: forward declaration of ‘class B’
class B;
^
According to the similar posts, that behaviour of gcc is correct due to paragraph 14.6.9 of the Standard. But that is counterintuitive since templates should be instantiated only where used. And the only usage occurs after all classes have been defined.
My question is what are the workarounds for this issue? Because using the member template is really convenient in this case. Maybe the problem is with the architecture or something else?
You can declare the parts of A that rely on B to be known, but define them later. The declaration then looks like this
class B;
class A
{
B *_parent;
public:
template <typename T> void Do(T val); /* (see below) */
A(B *parent) : _parent(parent) {}
};
then comes the definition of B
class B : public A { /* As before. */ };
and finally you define the missing pieces
template <typename T> void A::Do(T val)
{
_parent->DoB(val); /* Fine, B is known. */
}
As a side note, the original code compiled with just a warning using gcc 8.
You can add extra layer:
class A
{
B *_parent;
private:
template <typename TB, typename T>
static void CallDoB(TB* b, T&& val) {
b->DoB(std::forward<T>(val));
}
public:
template <typename T>
void Do(T val)
{
CallDoB(_parent, val);
}
A(B *parent) : _parent(parent) {}
};
now b-> is dependent of template and is postponed until real instantiation.
Related
I have a template class A<T> and its specialization for integral arguments. And both the class and its specialization declare the method foo(), which I would like to define outside of class bodies:
#include <concepts>
template<class T>
struct A { static void foo(); };
template<std::integral T>
struct A<T> { static void foo(); };
template<class T>
void A<T>::foo() {}
template<std::integral T>
void A<T>::foo() {}
int main() { A<int>::foo(); }
GCC accepts this code.
Clang prints the error https://gcc.godbolt.org/z/hYfYGPfMh :
error: type constraint differs in template redeclaration
template<std::integral T>
And MSVC prints errors on both method definitions:
error C3855: 'A<T>': template parameter 'T' is incompatible with the declaration
error C2447: '{': missing function header (old-style formal list?)
error C2065: 'foo': undeclared identifier
Please suggest how to define methods outside class bodies and make all the compilers happy?
I'm pretty sure that both the MS and Clang compilers are in error here and GCC is compiling your code correctly. Until these bugs are fixed in the other compilers, I suggest to continue using the concept pattern instead of going back to outdated methods. Simply work around the bug using an additional class:
#include <concepts>
#include <iostream>
// This is a work-around for using concept specialization of
// classes in conjunction with out-of-body definition of members.
// Only helpful for MSVC and Clang. GCC is properly compiling
// out-of-body concept specializations.
template <typename T>
class A
{
// For MSVC ONLY: the default template seems require being empty
// for this to work, but do fiddle around with it.
// (Also works with MSVC:)
A() = delete;
A(const A&) = delete;
A(A&&) noexcept = delete;
A& operator =(const A&) = delete;
A& operator =(A&&) noexcept = delete;
~A() = delete;
// Clang and GCC can have members just fine:
// static const char* foo();
};
// We use distinct base classes to define our concept specializations of A.
template <std::signed_integral T>
class A_Signed_Integral
{
public:
static const char* foo();
};
template <std::unsigned_integral T>
class A_Unsigned_Integral
{
public:
static const char* foo();
};
// And then we wrap them using the actual concept specializations of A,
// making the specializations effectivel the same class as the base class.
template <std::signed_integral T>
class A<T> :
public A_Signed_Integral<T>
{
public:
using A_Signed_Integral<T>::A_Signed_Integral; // grab all ctors
using A_Signed_Integral<T>::operator =; // an exceptional case
};
template <std::unsigned_integral T>
class A<T> :
public A_Unsigned_Integral<T>
{
public:
using A_Unsigned_Integral<T>::A_Unsigned_Integral;
using A_Unsigned_Integral<T>::operator =;
};
// Out-of-body definitions can be located to another file
template <std::signed_integral T>
inline const char* A_Signed_Integral<T>::foo()
{
return "using A<std::signed_integral T> foo";
}
template <std::unsigned_integral T>
inline const char* A_Unsigned_Integral<T>::foo()
{
return "using A<std::unsigned_integral T> foo";
}
int main()
{
std::cout << A<signed long>::foo() << std::endl;
std::cout << A<unsigned long>::foo() << std::endl;
return 0;
}
(Tested with all three compilers and works fine: see gcc.godbolt.org)
In the future, once the bugs are fixed it should be relatively easy with search and replace to erase the base classes in favor of just using the concept specializations of A.
EDIT:
Updating the example to work for MSVC, which cannot seem to make use of the default template yet.
I am not a C++ template expert, I tried something like below
template<class T, bool = std::is_integral_v<T>>
struct A
{};
template<class T>
struct A<T, false>
{
static void foo();
};
template<class T>
struct A<T, true>
{
static void foo();
};
template<class T>
void A<T,false>::foo()
{
std::cout << "I am working on a non-integral type" << std::endl;
}
template<class T>
void A<T, true>::foo()
{
std::cout << "I am working on an integral type" << std::endl;
}
int main()
{
A<int>::foo();
A<float> ::foo();
return 0;
}
and the code gave me the results on MS C++ compiler
I am working on an integral type
I am working on a non-integral type
I cannot compile my program with VS2015 due to a function using a templated parameter and inheritance.
The error is this one.
I'm trying to achieve the following :
class A
{
//do something
};
class B : public A
{
//do something
};
template <typename T>
class Foo {
template <typename T>
friend void function(Foo<T> & sm) {
//do something
}
};
void main()
{
Foo<A> test;
Foo<B> test2;
};
I do understand the meaning of the error, but I do not understand why it actually happens.
I suppose function is created with two different signatures :
void function(Foo<A> & sm); and
void function(Foo<B> & sm);
How is that a multi-definition?
EDIT - The full error message :
Error C2995 'void function(Foo<T> &)': function template has already been defined
EDIT² - From scratch
Both Clang and MS have the same complaint. Remove the second template specifier and it will compile.
class A{};
class B : public A{};
template <typename T>
class Foo {
// template <typename T>
friend void function(Foo<T> & sm) {
}
};
int main()
{
Foo<A> test;
Foo<B> test2;
};
T is already specified for the class Foo so its friend function is covered. You would us a second template if there were a difference at the function, like:
class A{};
class B : public A{};
template <typename T>
class Foo {
template <typename U>
friend void function(Foo<T> & sm, U another) {
}
};
int main()
{
Foo<A> test;
Foo<B> test2;
};
I'm just starting my journey into more advanced template code. Consider the following...
template <typename T>
class node_base
{
public:
T member;
node_base() {}
void do_the_thing(node_base& other)
{
std::cout << other.member;
}
};
template <typename ...Args>
class node : public node_base<Args>...
{
public:
node() :node_base<Args>()...{}
};
int main()
{
node<int> int_node;
node<int, double> double_node;
int_node.do_the_thing(double_node);
double_node.do_the_thing(int_node); // ambiguous call
std::cin.ignore();
return 0;
}
Using Visual Studio 2017 15.4.5, I get the following...
error C2385: ambiguous access of 'do_the_thing' note: could be the
'do_the_thing' in base 'node_base' note: or could be the
'do_the_thing' in base 'node_base'
My understanding is that the compiler should be able to deduce the correct function based on the argument, in this case node<int>. Why is this call considered ambiguous? What can I do to clarify the call while still maintaining this template pattern?
It's not about templates. You can reproduce it with the following:
struct A1 { void foo(); };
struct A2 { void foo(); };
struct B : A1, A2 {};
int main()
{
B b;
b.foo();
}
The relevant part of the Standard is
If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed.
So, you have two subobjects of types node_base<int> and node_base<double>.
EDIT: To address changes in the question and as DeanSeo deleted his solution, i will post it here:
template <typename T>
class node_base
{
public:
T member = sizeof(T);
node_base() {}
void do_the_thing(node_base& other) {
std::cout << other.member << std::endl;
}
};
template <typename ...Args>
class node : public node_base<Args>...
{
public:
node() :node_base<Args>()...{}
template <class T>
void do_the_thing(node_base<T>& other) {
node_base<T>::do_the_thing(other);
}
};
int main()
{
node<int> int_node;
node<double, int> double_node;
int_node.do_the_thing<int>(double_node);
double_node.do_the_thing(int_node);
double_node.do_the_thing<double>(double_node);
double_node.do_the_thing<int>(double_node);
return 0;
}
If I have a class A
template <typename T>
class A { public: void print() const; };
I can write specific version of my methode print for specific template values my doing
template<> void A<bool>::print() const { printf("A w/ type bool\n"); }
template<> void A<int>::print() const { printf("A w/ type int\n"); }
and the calling the method print will just call the code of the good implementation (of the compiler tell me if I don't have an implementation for a specific template.
Now, if I have multiples types in my class B's template
template <typename T1, typename T2>
class B { public: void print() const; };
and if I try to do the same as before, let's say for T2
template<typename T1> void B<T1,bool>::print() const { printf("B w/ type bool\n"); }
I get an compiler error :
error: invalid use of incomplete type 'class B<T1,bool>'
error: declaration of 'class B<T1, bool>'
What am I doing wrong ?
EDIT
My real life B class contains other methods with I do not want to specify (they work in the general case)
Having a partially specified class decalred makes that those generic methods aren't natively availlable
You can't partial specialize a function/method.
But you can partial specialize the whole class:
template <typename T1, typename T2> class B;
template<typename T1> class B<T1, bool>
{
public:
void print() const { printf("B w/ type bool\n"); }
};
What am I doing wrong?
template<> void A<bool>::print() const { printf("A w/ type bool\n"); }
template<> void A<int>::print() const { printf("A w/ type int\n"); }
These member functions are like normal functions, they are not templates with un-substituted parameters, so you are just providing definitions for the symbols, which will be used when those functions get called. (And like normal functions, if those definitions are in a header and you don't declare them inline you will get multiple definitions errors for them.)
template<typename T1> void B<T1,bool>::print() const { printf("B w/ type bool\n"); }
This is not the same, this is providing a definition for a member function of a class template partial specialization. i.e. it's a template that will be used to generate code for the member of that partial specialization, but you haven't declared any such partial specialization, so you can't define its members.
You can make it compile by defining the partial specialization first:
// primary template
template <typename T1, typename T2>
class B { public: void print() const; };
// partial specialization
template<typename T1>
class B<T1,bool> { public: void print() const; };
template<typename T1> void B<T1,bool>::print() const { printf("B w/ type bool\n"); }
However it is often inconvenient to have to repeat the entire class template definition just to define a partial specialization for one or two members, so it might be worth taking one of the alternative designs shown in other answers.
With templates it's best to decompose each part of the specialisation into its own template function or traits class.
Here's a clean way to do what you want:
template<typename T>
const char* type_name()
{
return "unknown";
};
template<>
const char* type_name<int>()
{
return "int";
}
template<>
const char* type_name<bool>()
{
return "bool";
}
struct foo {};
template<>
const char* type_name<foo>()
{
return "my custom foo";
}
struct bar {};
template <typename T>
class A {
public:
void print() const {
cout << "A w/ type " << type_name<T>() << '\n';
}
};
int main() {
A<bool> ab;
A<int> ai;
A<foo> af;
A<bar> abar;
ab.print();
ai.print();
af.print();
abar.print();
return 0;
}
output:
A w/ type bool
A w/ type int
A w/ type my custom foo
A w/ type unknown
Program ended with exit code: 0
With tag dispatching, you might do:
#include <iostream>
template<typename A, typename B>
class X
{
private:
template <typename U> struct Tag {};
template <typename U>
void print(Tag<U>) const;
void print(Tag<bool>) const { std::cout << "bool\n"; }
void print(Tag<int>) const{ std::cout << "int\n"; }
public:
void print() const { print(Tag<B>()); }
};
int main()
{
X<void, bool>().print();
X<void, int>().print();
}
The following code (condensed from a larger program) does not compile with clang or gcc.
struct S1 {
void m1() {}
};
template<typename B> struct S2 : B {
void m2() {}
void m3();
};
template<typename S, void (S::*m)()> void f1(S* o) {
(o->*m)();
}
template<typename B> void S2<B>::m3() {
f1<S2, &S2::m1>(this);
}
int main() {
void (S2<S1>::*m)() = &S2<S1>::m1;
S2<S1> o;
o.m3();
}
Here is clang's error message:
bad.cc:15:3: error: no matching function for call to 'f1'
f1<S2, &S2::m1>(this);
^~~~~~~~~~~~~~~
bad.cc:21:5: note: in instantiation of member function 'S2<S1>::m3' requested
here
o.m3();
^
bad.cc:10:43: note: candidate template ignored: invalid explicitly-specified
argument for template parameter 'm'
template<typename S, void (S::*m)()> void f1(S* o) {
^
1 error generated.
This code compiles when I replace m1 by m2. Clearly the compiler knows about m1 (different message when I replace m1 by m4), so why should a pointer to it be invalid in this context?
The thing is, the type of m1 is void(S1::*)(void), not void(S2::*)(void). So fix it by leveraging the known base class name:
struct S1 {
void m1() {}
};
template<typename B> struct S2 : B {
void m2() {}
void m3();
};
template<typename S, typename B, void (B::*m)(void)> void f1(S* o) {
(o->*m)();
}
template<typename B> void S2<B>::m3() {
f1<S2, B, &B::m1>(this);
}
int main() {
S2<S1> o;
o.m3();
}
Of course this doesn't (yet) scale to methods defined in indirect base classes, but with a bit of TMP it can be done (will see if I can post that while the intermission of Going Native 2012 lasts :))
The more 'flexible' approach would be:
template<typename B, typename MF> void f1(B* o, MF mfp) {
(o->*mfp)();
}
template<typename B> void S2<B>::m3() {
f1(this, &B::m1);
}
You could/should use typetraits to ensure that S2<B>& is convertible to a B& if the class layout doesn't already explicitly guarantee that, as in your current example.