CRTP fails w/ decltype - c++

template<typename T> struct A {
auto func() -> decltype(T::func()) {
return T::func();
}
};
class B : public A<B> {
void func() {
}
};
Seems pretty simple to me. But MSVC fails to compile.
visual studio 2010\projects\temp\temp\main.cpp(4): error C2039: 'func' : is not a member of 'B'
visual studio 2010\projects\temp\temp\main.cpp(8) : see declaration of 'B'
visual studio 2010\projects\temp\temp\main.cpp(8) : see reference to class template instantiation 'A<T>' being compiled
with
[
T=B
]
visual studio 2010\projects\temp\temp\main.cpp(4): error C3861: 'func': identifier not found
Even though the compiler will happily accept calling the function. The below sample compiles fine.
template<typename T> struct A {
void func() {
return T::func();
}
};
class B : public A<B> {
void func() {
}
};
I've got the same issue trying to use any types from the template argument.
template<typename T> struct A {
typedef typename T::something something;
};
class B : public A<B> {
typedef char something;
};
visual studio 2010\projects\temp\temp\main.cpp(4): error C2039: 'something' : is not a member of 'B'
Whereas class B clearly defines a type called "something". The compiler is perfectly happy to call functions on an object of type T, T& or T*, but I can't seem to access any types from T.

You are trying to use T::func before it was declared. That's why the compiler shouts at you. Notice that when you derive from a class, the class is generated if it comes from a class template. And the implicit generation of the class (which is called implicit instantiation) necessiates the generation of declarations for all its members (so the compiler knows the sizeof value of the class, and can perform lookup into it).
So it also instantiates the declaration auto func() -> decltype(T::func()) and surely fails here.

There seem to be several issues with your code, one of which looks like a VS10 bug.
You're calling T::func() from A without casting A to T, this is needed as part of CRTP since A doesn't derive from T. -- FixL return static_cast<T*>(this)->func();
What you're passing to decltype looks like a static function invocation while func is in fact an instance function. Since decltype doesn't actually run the function you should do something like this decltype(static_cast<T*>(nullptr)->func())
func is private in B and can't be called from A -- Fix: change A to be a struct
This looks like a bug in VS10, even after all these fixes I get an error that you're trying to use an undefined class B in the decltype.
As a workaround can you refactor func out into a base class? (now we need two template parameters, one for casting to and one for decltype thus creating a new idiom CRTPEX)
struct Base {
void func() { }
};
template<typename T, typename U> struct A {
auto func() -> decltype(static_cast<T*>(nullptr)->func()) {
return static_cast<U*>(this)->func();
}
};
struct B : public A<Base, B>, public Base {
};
I see that g++ also chokes on this decltype can anyone confirm that this is a defect? If so I will open a bug for Microsoft. It is my understanding that the following code is valid but neither g++ nor VC10 compile it.
template<typename T> struct A {
auto func() -> decltype(static_cast<T*>(nullptr)->func()) {
return static_cast<T*>(this)->func();
}
};
struct B : public A<B> {
void func() {}
};

First, I think that the close-to-proper code is:
template<typename T> struct A {
auto func()
-> decltype(static_cast<T*>(this)->func())
{
return static_cast<T*>(this)->func();
}
};
class B : public A<B> {
void func(){
}
};
As Motti pointed out. However that still fails, and I think for the reason that the return type of the base has to be known when B is declated to inherit from A<B>, but since B is not defined yet, it becomes a chicken and egg problem.
However, it may be finally be possible in C++1y by using simply auto (without decltype), I tried with gcc-4.8.2
template<typename T> struct A {
auto func()
//c++1y// -> decltype(static_cast<T*>(this)->func())
{
return static_cast<T*>(this)->func();
}
};
class B : public A<B> {
void func(){
}
};
This compiles (c++ -std=c++1y) and runs:
int main(){
B b; b.func();
}
Two disclaimers: I don't know why this works. I don't know how standard it is.

Related

C++ 'using' keyword in class hierarchy with function call operator and private inheritance

I have stumbled upon something that I don't quite understand. I have a class hierarchy that uses private inheritance where each of the structs defines a different function call operator. Oddly enough, the function call operator from the topmost struct is available in the most derived struct, despite the fact that a using directive is only used in the first derived struct. A regular function foo, though, is not accessible there, as expected. Example:
#include <string>
#include <vector>
#include <iostream>
struct A {
void foo() {}
void operator()(bool) {
std::cout << "bool\n";
}
};
struct B : private A {
using A::foo;
using A::operator();
void operator()(std::string) {}
};
struct C : private B {
using B::operator();
void operator()(std::vector<int>) {}
};
struct D : private C {
using C::operator();
void operator()(std::vector<double>) {}
};
int main() {
D d{};
d(false); // <-- works!
//d.foo(); // <-- error: ‘void A::foo()’ is private within this context
return 0;
}
I happened upon this while trying to implement the C++17 overload object for use with boost::apply_visitor using pre-C++17 code. I solved it using recursive inheritance, where each object pulls in the function call operator of its direct base class like so:
template<typename T, typename... Ts>
struct visitor : private T, private visitor<Ts...> {
using T::operator();
using visitor<Ts...>::operator();
visitor(T func, Ts... tail) : T{ std::move(func) }, visitor<Ts...>{ std::move(tail)... } {}
};
template<typename T>
struct visitor<T> : private T {
using T::operator();
visitor(T func) : T{ std::move(func) } {}
};
template<typename... Ts>
visitor<Ts...> make_visitor(Ts&&... funcs) {
return visitor<Ts...>{ std::forward<Ts>(funcs)... };
}
I wanted to understand why all of the operators are available in the most derived object. That's how I came up with the above example. Compiler is g++ 11.1.0.
Can anyone enlighten me as to what's going on here?
As pointed out by others in comments, it turns out I just had an error in my thinking. The using pulls in all the operators available in the respective base class, including the ones that were imported by the base class itself, and therefore all the operators will be available in the bottommost object. foo, on the other hand, is just handed down to B.

a call to a method from base template fails to compile [duplicate]

This question already has an answer here:
Why can I call base template class method from derived class
(1 answer)
Closed 2 years ago.
I have a template struct which inherits another template struct. It fails compilation in different compilers (in VS2017 and clang on linux). A simple change in the code fixes it.
The code is:
template<typename T>
struct base {
int GetValue2() { return 0; }
};
template<typename T>
struct derived : public base<T>
{
int GetValue() { return GetValue2(); }
};
int main() {
derived<int> a;
return 1;
}
If I change the line
int GetValue() { return GetValue2(); }
to:
int GetValue() { return this->GetValue2(); }
everything compiles just fine.
Does anyone have an idea what's going on?
There is a two-stage name lookup here.
In GetValue(), GetValue2 is not used in a dependent context, so the compiler will look for a name declared at the enclosing namespace scope (which is the global scope here).
It will not look into the base class, since that is dependent and you may declare specializations of Base even after declaring Derived, so the compiler can't really know what i would refer to. If there is no global variable i, then you will get an error message.
In order to make it clear that you want the member of the base class, you need to defer lookup until instantiation time, at which the base class is known. For this, you need to access i in a dependent context, by either using this->i (remember that this is of type Derived*, so is obviously dependent), or using Base::i. Alternatively, Base::i might be brought into scope by a using-declaration.
template<typename T>
struct base {
int GetValue2() { return 0; }
};
template<typename T>
struct derived : public base<T>
{
int GetValue_using() {
using base<T>::GetValue2; // won't compile with gcc, place it outside the function (ok with clang and msvc)
return GetValue2();
}
int GetValue_this() { return this->GetValue2(); }
int GetValue_base() { return base<T>::GetValue2(); }
};
int main() {
derived<int> a;
return 1;
}

template class method instantiation when a virtual unrelated method in the base class causes compilation failure on MSVC

Is the following code legal C++?
MS Visual C++ fails, but gcc and clang are fine: https://godbolt.org/z/vsQOaW
It could be an msvc bug, but wanted to check first:
struct Base {
virtual void junk() = 0;
};
template <class T>
struct Derived : Base {
void junk() override {
T::junkImpl();
}
void otherMethod() {
}
};
template <class T>
struct NotDerived {
void junk() {
T::junkImpl();
}
void otherMethod() {
}
};
struct TypeWithJunk {
void junkImpl() {
}
};
struct TypeWithoutJunk {};
void reproduce(NotDerived<TypeWithoutJunk>* ndt, Derived<TypeWithoutJunk>* dt) {
// works - junk is not used, not instantiated
ndt->otherMethod();
// fails on MSVC - junk is instantiated even if not used
dt->otherMethod();
}
junk may get instantiated just like the rest of virtual functions because it is required to populate vtable. So all the compilers seem to demonstrate conforming behavior:
17.8.1 Implicit instantiation [temp.inst]
9 … It is unspecified
whether or not an implementation implicitly instantiates a virtual member function of a class template if
the virtual member function would not otherwise be instantiated.

Why is it that defining boost::shared_ptr of a templated behaves differently than boost::shared_ptr of a non templated class

I was trying to integrate the boost::share_ptr into a pair of templated classes that were originally derived from a boost::asio example I found. When I define a type within one class which is a shared::ptr of that class. I can't seem to reference the type in another templated class. If I remove templates from the code, it all compiles.
This won't compile:
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
using namespace std;
template <typename TSomething1>
class SomeTemplateT : public boost::enable_shared_from_this<SomeTemplateT<TSomething1> >
{
public:
typedef boost::shared_ptr<SomeTemplateT<TSomething1> > Ptr;
static Ptr Create()
{
return Ptr(new SomeTemplateT<TSomething1>());
}
SomeTemplateT()
{
cout << "SomeTemplateT created" << endl;
}
};
template <typename TSomething>
class OtherTemplateT
{
public:
OtherTemplateT()
{
// COMPILATION ERROR HERE
SomeTemplateT<TSomething>::Ptr someTemplate = SomeTemplateT<TSomething>::Create();
}
private:
};
The code above yields the following compilation error:
src\Templates\main.cpp: In constructor 'OtherTemplateT<TSomething>::OtherTemplateT()':
src\comps\oamp\src\Templates\main.cpp:30: error: expected ';' before 'someTemplate'
Taking virtually the same code without templates compiles without difficulty:
class SomeTemplateT : public boost::enable_shared_from_this<SomeTemplateT>
{
public:
typedef boost::shared_ptr<SomeTemplateT> Ptr;
static Ptr Create()
{
return Ptr(new SomeTemplateT());
}
SomeTemplateT()
{
cout << "SomeTemplateT created" << endl;
}
};
class OtherTemplateT
{
public:
OtherTemplateT()
{
SomeTemplateT::Ptr someTemplate = SomeTemplateT::Create();
}
private:
};
Platform information:
I'm using gcc4.4.0 from MinGW on windows XP (Code:Blocks IDE).
Am I doing something wrong?
EDIT:
I forgot to mention that if I replace the use of the Ptr typedef with the full declaration of the shared ptr:
boost::shared_ptr
Everything compiles fine.
Also, I can use the type in code outside the of the template.
SomeTemplateT<TSomething>::Ptr is a dependent name; that is, its definition depends on the template parameter. The compiler can't assume that it's a type name unless you say so:
typename SomeTemplateT<TSomething>::Ptr someTemplate = SomeTemplateT<TSomething>::Create();
^^^^^^^^
You need to use typename:
typename SomeTemplateT<TSomething>::Ptr someTemplate = SomeTemplateT<TSomething>::Create();
This is required to make parsing possible without semantic analysis. Whether SomeTemplateT<TSomething>::Ptr is a type or a member is not known until SomeTemplateT<TSomething> has been compiled.
A example taken from the C++11 Standard (n3290) that demonstrate why the keyword typename (in this context) is useful.
( 14.6 Name resolution [temp.res] )
struct A
{
struct X { };
int X;
};
struct B
{
struct X { };
};
template<class T> void f(T t)
{
typename T::X x;
}
void foo()
{
A a;
B b;
f(b); // OK: T::X refers to B::X
f(a); // error: T::X refers to the data member A::X not the struct A::X
}

C++ class template inheritance puzzle

What's wrong with this code? gcc 4.6.1 is complaining "‘foo’ was not declared in this scope" in baz(). If I transform the code so that one of the templates is just a regular class, the problem goes away.
struct Foo {
char foo;
};
template<int N>
struct Bar : public Foo
{
Bar() { foo; }
};
template<int N>
struct Baz : public Bar<N>
{
void baz() { foo; }
};
int main() {
Baz<10> f;
return 0;
}
What is wrong, according to the specifications, I don't know, but you may make your code to compile by using:
void baz() { Bar<N>::foo; }
foo is a dependent name; that is, it depends on the template parameter, so until the template is instantiated the compiler doesn't know what it is. You have to make it clear that it is a class member, either Bar<N>::foo or this->foo.
(You probably also want to do something with it; simply using it as the ignored value of an expression doesn't do anything at all).