While working on my own type erasure iterator, I ran into an issue where the compiler (MSVC10) crashed with a stack overflow on this code:
struct base {}; //In actual code, this is a template struct that holds data
template<class category, class valuetype>
struct any; //In actual code, this is abstract base struct
template<class basetype, class category, class valuetype>
struct from; //In actual code, this is function definitions of any
template<class valuetype>
struct any<void,valuetype>
{ void a() {} };
template<class category, class valuetype>
struct any
: public any<void,valuetype> //commenting this line makes it compile
{ void b() {} };
template<class basetype, class valuetype>
struct from<basetype,void,valuetype>
: public base //commenting out _either_ of these makes it compile
, public any<void,valuetype>
{ void c() {} };
int main() {
from<int, void, char> a;
a.a();
a.c();
any<int, char> b;
b.a();
b.b();
return 0;
}
Obviously, I've removed everything I can where the bug remains. (Origional code was 780+ lines) Removing any remaining template parameters causes the code to compile.
The full error message is:
main.cpp(23): fatal error C1063: compiler limit : compiler stack overflow
main.cpp(20) : see reference to class template instantiation 'from<basetype,void,valuetype>' being compiled
IDEOne compiles it fine. I've heard that MSVC implemented two-phase lookup wrong, which seems relevant, but doesn't explain why it compiles when I remove the line that makes from inherit from base. Can anyone teach me why MSVC10 won't compile this? What did I do that I should be avoiding?
As a workaround, consider introducing an additional class between the unspecialized any and the specialization with category = void:
template <class valuetype>
class detail_void_any
: public any<void, valuetype>
{
};
template<class category, class valuetype>
class any
: public detail_void_any<valuetype>
{
};
The following complete program should compile without error:
class base {}; // Data Holder (in reality it's templated, so required)
template<class category, class valuetype>
class any; // Virtual Function Interface
template<class basetype, class category, class valuetype>
class from; // Virtual Function Implementation
template<class valuetype>
class any<void,valuetype>
{};
template <class valuetype>
class detail_void_any
: public any<void, valuetype>
{
};
template<class category, class valuetype>
class any
: public detail_void_any<valuetype>
{
};
template<class basetype, class valuetype>
class from<basetype,void,valuetype>
: public base //commenting out _either_ of these makes it compile
, public any<void,valuetype>
{}; //this is line 23, where the compiler crashes
int main() {return 0;}
Well I give up but I did manage to generate a warning:
template <typename T1, typename T2>
class Any; // forward
template <typename T2>
class Any<void, T2> // partial spec of forward
{};
template <typename T1, typename T2>
class Any: // template definition
public Any<void, T2> // inherit from partial spec
{};
template <typename T1, typename T2>
class From :
public Any<int, T2>, // inherit definition
public Any<void, T2> // inherit definition or partial spec?
// with spec first we get fatal error C1063: compiler limit : compiler stack overflow (CRASH!)
// with definition first we get warning C4584: 'From<T1,T2>' : base-class 'Any<void,T2>' is already a base-class of 'Any<int,T2>'
{};
int main()
{
return 0;
}
Simplest workaround: Replace:
template<class category, class valuetype>
class any : public any<void, valuetype>
{
};
with:
template<class valuetype, class category>
class any : public any<void, valuetype>
{
};
Related
I have a CRTP Base class (Bar) which is inherited by a unspecified class. This Derived class may or may not have specific member (internal_foo), and this specific member my or may not have another member (test()).
In this scenario internal_foo will always be public, however test() is private, but declaring Bar as a friend.
I can detect internal_foo using traits fine, because it is public. But I cannot detect test() due to it being private, even though Bar is a friend.
The below example works due to test() being public:
template<class, class = void >
struct has_internal_foo : std::false_type {};
template<class T>
struct has_internal_foo<T,
void_t<
decltype(std::declval<T>().internal_foo)
>> : std::true_type {};
template<class, class = void>
struct internal_foo_has_test : std::false_type {};
template<class T>
struct internal_foo_has_test<T,
void_t<decltype(std::declval<T>().internal_foo.test())
>> : std::true_type {};
class InternalFoo
{
public:
void test()
{
}
};
class BadInternalFoo
{
};
template<class T>
class Bar
{
public:
template<class _T = T>
std::enable_if_t<conjunction<has_internal_foo<_T>, internal_foo_has_test<_T>>::value, void>
action()
{
static_cast<T&>(*this).internal_foo.test();
}
};
class Foo :
public Bar<Foo>
{
public:
InternalFoo internal_foo;
};
class BadFoo :
public Bar<BadFoo>
{
public:
BadInternalFoo internal_foo;
};
void test()
{
Foo foo;
BadFoo bad_foo;
foo.action(); // Compiles. As expected.
bad_foo.action(); // Does not compile. As expected.
}
However this next version does not work, due to test() being private:
template<class, class = void >
struct has_internal_foo : std::false_type {};
template<class T>
struct has_internal_foo<T,
void_t<
decltype(std::declval<T>().internal_foo)
>> : std::true_type {};
template<class, class = void>
struct internal_foo_has_test : std::false_type {};
template<class T>
struct internal_foo_has_test<T,
void_t<decltype(std::declval<T>().internal_foo.test())
>> : std::true_type {};
class InternalFoo
{
public:
template<class T>
friend class Bar;
template<class, class>
friend struct internal_foo_has_test;
private:
void test()
{
}
};
class BadInternalFoo
{
};
template<class T>
class Bar
{
public:
template<class _T = T>
std::enable_if_t<conjunction<has_internal_foo<_T>, internal_foo_has_test<_T>>::value, void>
action()
{
static_cast<T&>(*this).internal_foo.test();
}
};
class Foo :
public Bar<Foo>
{
public:
InternalFoo internal_foo;
};
class BadFoo :
public Bar<BadFoo>
{
public:
BadInternalFoo internal_foo;
};
void test()
{
Foo foo;
BadFoo bad_foo;
foo.action(); // Does not compile
bad_foo.action(); // Does not compile
}
As seen above, I have tried to friend the detection struct too, but that didn't help.
Is there a way to do what I am trying to do?
Ideally I would like this solution to be portable, and not use anything beyond C++11, 14 at the most. (I have implemented void_t & conjunction)
Edit:
The suggested question does not answer this one. That question wants to detect whether a member is public or private, and only access it if it is public, I wish for the detection to return positive on a private member of a friend class.
Summary and the fix
Looks like a GCC 11 bug and your second attempt should in fact work.
However, I recommend rewriting action's definition in either of two ways so you don't even need the member detection idiom:
// Way 1
template<class _T = T>
decltype(std::declval<_T&>().internal_foo.test()) action() {
static_cast<T&>(*this).internal_foo.test();
}
// Way 1, with a different return type via the comma operator
template<class _T = T>
decltype(std::declval<_T&>().internal_foo.test(), std::declval<ReturnType>()) action() {
static_cast<T&>(*this).internal_foo.test();
}
// Way 2
template<class _T = T>
auto action() -> decltype(static_cast<_T&>(*this).internal_foo.test()) {
static_cast<_T&>(*this).internal_foo.test(); // Using _T for consistency
}
Note that I use _T inside the decltype so it's dependent on the template argument and can be SFINAEd. Also note that it's still possible to specify an arbitrary return return type without any enable_ifs.
Details
I took the liberty to prepend #include <type_traits> and using namespace std; to both of your examples and using C++17 so they can be compiled.
Some findings from the comments section:
Your first code (does not) compile(s) as expected with Clang 14, gcc 11 and gcc trunk: https://godbolt.org/z/EbaYvfPE3
Your second code (does not) compile(s) as expected with Clang add gcc trunk, but gcc 11 differs: https://godbolt.org/z/bbKrP8Mb9
There is an easier reproduction example: https://godbolt.org/z/T17dG3Mx1
#include <type_traits>
template<class, class = void>
struct has_test : std::false_type {};
template<class T>
struct has_test<T, std::void_t<decltype(std::declval<T>().test())>> : std::true_type {};
class HasPrivateTest
{
public:
template<class, class>
friend struct has_test;
friend void foo();
private:
void test() {}
};
// Comment the following line to make it compile with GCC 11
static_assert(has_test<HasPrivateTest>::value, "");
void foo() {
static_assert(has_test<HasPrivateTest>::value, "");
}
static_assert(has_test<HasPrivateTest>::value, "");
The code above compiles with Clang 14 and gcc trunk, but is rejected by gcc 11 with three "static assertion failed" messages, one for each assertion. However, commenting out the first static_assert makes all three compilers accept the code.
So it seems like GCC 11 (and earlier) tries to instantiate the template and do access checks depending on the context. Hence, if the first instantiation is outside of a friend, .test() method is not accessible, and the result is kind cached. However, if it's inside the friend void foo(), .test() is accessible and all static_asserts succeed.
#Klaus have pointed out a recent GCC bug whose fix seems relevant: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96204
I stumbled into a scenario and I'm trying to figure out for the cleanest approach, if there's any.
I have a template class with a protected constructor, that needs to be instantiated by a friend template class. Both share part of template parameters, but not all. Here's a example of my problem.
I wish to know from experienced programmers if there are other possible solutions (I suppose not, besides turning constructor public), and if between the two I present one its more acceptable than the other.
Thanks
Solution 1- I supply "unnecessary" template parameters to the class with protected constructor (class Element).
template <typename Tp_>
class Engine_Type_X
{
};
template <typename Tp_>
class Engine_Type_Z
{
};
//Forward declaration
template <typename Tp_, template<typename> typename Eng_>
class Container;
//Eng_ is only required to declare the friend class
template <typename Tp_,template<typename> typename Eng_>
class Element
{
friend class Container<Tp_,Eng_>;
Tp_ tp_;
protected:
Element(Tp_ tp) : tp_{tp} //protected ctor!!!
{}
};
template <typename Tp_, template<typename> typename Eng_>
class Container
{
using Element_tp = Element<Tp_,Eng_>;
using Engine_tp = Eng_<Tp_>;
std::vector<Element_tp> container_;
Engine_tp &engine_;
public:
Container(Engine_tp &engine) : container_{},engine_{engine}
{}
void install(Tp_ tp)
{ Element_tp elem{tp};
container_.emplace_back(elem);
}
};
Solution 2 - I use an approach like the one I've found here How to declare a templated struct/class as a friend?
template <typename Tp_>
class Engine_Type_X
{
};
template <typename Tp_>
class Engine_Type_Z
{
};
template <typename Tp_>
class Element
{
template<typename,template<typename>typename> friend class Container; //All templated classes are friend
Tp_ tp_;
protected:
Element(Tp_ tp) : tp_{tp} //protected ctor!!!
{}
};
template <typename Tp_, template<typename> typename Eng_>
class Container
{
using Element_tp = Element<Tp_>;
using Engine_tp = Eng_<Tp_>;
std::vector<Element_tp> container_;
Engine_tp &engine_;
public:
Container(Engine_tp &engine) : container_{},engine_{engine}
{}
void install(Tp_ tp)
{ Element_tp elem{tp};
container_.emplace_back(elem);
}
};
You still have some options to explore.
You could make one class an inner class (called nested class), that would automaticly
friend it to the 'outside' class. See https://en.cppreference.com/w/cpp/language/nested_types
Another approach is to require a so called 'token' as a parameter to a
the constructor, this token type doesn't usually take template parameters, then make it so that this token can only be
created by the other class (could be a nested type or friended).
On request from OP, here is an outline of one way to accomplish 2. option: (using c++0x)
template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};
template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref>: std::true_type {};
template <class T>
class create_token {
public:
typedef T Type;
//copy of token not allowed !
create_token(const create_token &) = delete;
create_token& operator=(const create_token &) = delete;
//move is fine
create_token(create_token &&) = default;
create_token& operator=(create_token &&) = default;
friend class T;
private:
create_token();
};
template<class BlaBlaBla>
struct Element {
template<class T>
Element(create_token<T> t) {
static_assert(std::is_specialization<create_token<T>::Type, Container>::value, "Wrong type of token provided");
}
};
template<class Whatever>
struct Container {
template<class T>
Element(create_token<T> t) {
static_assert(std::is_specialization<create_token<T>::Type, Element>::value, "Wrong type of token provided");
}
};
Suppose I have a class like so:
template<class T>
class Base{ };
Suppose I have another class like so:
template<class T, class Other>
class Derived :
public virtual Base<T>,
public virtual OtherRandomClass<Other>
{ };
Is there some way to create a template class to determine which version of Base (if any) a random class like Derived is derived from?
Here is a possible solution (working since C++11 - well, it works with C++14, but it does it with C++11 if you use Base<T> instead of auto as a return type for f):
#include<utility>
#include<type_traits>
template<class T>
class Base{ };
template<class T>
class OtherRandomClass{ };
template<class T, class Other>
class Derived :
public virtual Base<T>,
public virtual OtherRandomClass<Other>
{ };
template<typename T>
constexpr auto f(const Base<T> &b) { return b; }
template<typename T>
struct S {
using type = decltype(f(std::declval<T>()));
};
int main() {
static_assert(std::is_same<typename S<Derived<int, double>>::type, Base<int>>::value, "!");
}
It doesn't work if Derived inherits more than once from Base.
Using sfinae (something like the void_t idiom) one can even design a class that works similarly to enable_if: it has type only if T actually inherits from Base once.
It would have the following form:
template<typename T>
constexpr auto f(const Base<T> &b) { return b; }
template<typename...>
using void_t = void;
template<typename T, typename = void_t<>>
struct S { };
template<typename T>
struct S<T, void_t<decltype(f(std::declval<T>()))>> {
using type = decltype(f(std::declval<T>()));
};
This struct can be used at compile time for any template trickery you can imagine.
In both cases, S::type (if it exists) is the type of the base class from which Derived inherits, that is Base<T>.
See the static_assert in the main function of the example for further details.
#include <iostream>
#include <typeinfo>
// placeholder template
template<class SomeDerived> struct traits {};
template<class T>
class Base{ };
template<class T>
class OtherRandomClass{ };
template<class T, class Other>
class Derived :
public virtual Base<T>,
public virtual OtherRandomClass<Other>
{ };
// specialise for our class to provide introspection
template<
class T1,
class T2>
struct traits<
Derived<T1, T2>
>
{
using t1_type = T1;
using first_base_type = Base<T1>;
};
int main()
{
Derived<int, float> x;
using mytraits = traits<decltype(x)>;
std::cout << typeid(mytraits::t1_type).name() << std::endl;
std::cout << typeid(mytraits::first_base_type).name() << std::endl;
}
I'm having trouble understanding why the code below doesn't compile -- could someone please explain?
How do I access a typedef in a derived class from the base class?
template<class Derived>
struct Test
{
template<class T>
typename Derived::value_type foo(T);
};
struct Derived : public Test<Derived>
{
typedef int value_type;
};
At the time of declaring Derived, Derived is not yet a complete type -- you've only just started declaring it! Hence in the specialization Test<Derived>, the template argument is an incomplete type, and thus you mustn't refer to a nested name such as Derived::value_type -- that's circular logic.
You could decycle the problem by making the return type a separate argument:
template <typename T, typename R> struct Test
{
template <typename U> R foo(U);
};
template <typename R>
struct BasicDerived : public Test<BasicDerived, R>
{
typedef R value_type;
};
typedef BasicDerived<int> Derived;
You can't access typedefs or members of the template class directly in the base class, because at that point it's not a complete type. Allowing this would lead to circular behaviour:
template<class Derived>
struct Test
{
typedef typename Derived::value_type foo;
};
struct Derived : public Test<Derived>
{
typedef Test<Derived>::foo value_type;
};
You can however reference members of the template class within methods, as they are not instantiated until later:
template<class Derived>
struct Test
{
void foo() { typename Derived::value_type bar; }
};
struct Derived : public Test<Derived>
{
typedef int value_type;
};
Alternatively, depending on what you are trying for, you can pass the typedef as an additional template parameter:
template<typename value_type, class Derived>
struct Test
{
template<class T>
value_type foo(T);
};
struct Derived : public Test<int, Derived>
{
typedef int value_type;
};
Consider the following:
template <typename T>
class Base {
public:
template <typename U>
class Nested { };
};
template <typename T>
class Derived : public Base<T> {
public:
//How do we typedef of redefine Base<T>::Nested?
using Base<T>::Nested; //This does not work
using Base<T>::template<typename U> Nested; //Cannot do this either
typedef typename Base<T>::template<typename U> Nested Nested; //Nope..
//now we want to use the Nested class here
template <typename U>
Class NestedDerived : public Nested { };
//or like this:
Nested<int> nestedVar; // obviously does not work
};
How to use the templated Nested class in the Derived class? Is this possible to do in current version of C++ standard?
Actually using works as advertised, it just doesn't get rid of the dependent-name issue in the template and it can't currently alias templates directly (will be fixed in C++0x):
template <class T>
struct Base {
template <class U> struct Nested {};
};
template <class T>
struct Derived : Base<T> {
using Base<T>::Nested;
// need to prefix Nested with template because
// it is a dependent template:
struct X : Base<T>::template Nested<int> {};
// same here:
template<class U>
struct Y : Base<T>::template Nested<U> {};
// data member, typename is needed here:
typename Base<T>::template Nested<int> data;
};
void f() {
Derived<int>::Nested<int> n; // works fine outside
}
There is another possible gotcha when using Derived<T>::Nested in templates, but again that is a dependent-name issue, not inheritance-related:
template<class T>
void g() {
// Nested is a dependent type and a dependent template, thus
// we need 'typename' and 'template':
typedef typename Derived<T>::template Nested<int> NestedInt;
}
Just remember that names that depend on template arguments have to be
prefixed with typename if its a dependent type: typename A<T>::B
directly prefixed with template if its a dependent template: A<T>::template f<int>()
both if both: typename A<T>::template B<int>
typename is illegal in base-class-lists: template<class T> struct A : B<T>, C<T>::template D<int> {};
This seems to work:
(EDIT: added some more lines to show the first template statement. And thanks to Samir Talwar for correcting my formatting!)
template <typename T, typename U>
class Derived : public Base<T> {
public:
typedef typename Base<T>::template Nested<U> Nested;
class NestedDerived : public Nested { };
Nested nestedVar;
};
Try this:
template <typename T>
class Base {
public:
template <typename U>
class Nested { };
};
template <typename T>
class Derived : public Base<T> {
public:
//How do we typedef of redefine Base<T>::Nested?
//using Base<T>::Nested; //This does not work
//using Base<T>::template<typename U> Nested; //Cannot do this either
//typedef typename Base<T>::template<typename U> Nested Nested; //Nope..
//now we want to use the Nested class here
template <typename U>
class NestedDerived : public Base<T>::template Nested<U> { };
};
int main()
{
Base<int>::Nested<double> nested;
Derived<int>::NestedDerived<double> nested_derived;
return 0;
}
Compiled fine using gcc 4.3.3 on slackware 13
I'm still not 100% sure what you want, but you could try.
This compiled on Visual Studio
template <typename T>
class Base {
public:
template <typename U>
class Nested { };
};
template <typename T>
class Derived : public Base<T> {
public:
//now we want to use the Nested class here
template <typename U>
class NestedDerived : public Nested<U> { };
};
int _tmain(int argc, _TCHAR* argv[])
{
Base<int>::Nested<double> blah2;
Derived<int>::NestedDerived<int> blah;
return 0;
}