Disambiguate class-member in multiple inheritance - c++

Suppose I have this variadic base class-template:
template <typename ... Types>
class Base
{
public:
// The member foo() can only be called when its template
// parameter is contained within the Types ... pack.
template <typename T>
typename std::enable_if<Contains<T, Types ...>::value>::type
foo() {
std::cout << "Base::foo()\n";
}
};
The foo() member can only be called when its template-parameter matches at least one of the parameters of Base (the implementation of Contains is listed at the bottom at this post):
Base<int, char>().foo<int>(); // fine
Base<int, char>().foo<void>(); // error
Now I define a derived class that inherits twice from Base, using non-overlapping sets of types:
struct Derived: public Base<int, char>,
public Base<double, void>
{};
I was hoping that when calling e.g.
Derived().foo<int>();
the compiler would figure out which base-class to use, because it is SFINAE'd out of the one that does not contain int. However, both GCC 4.9 and Clang 3.5 complain about an ambiguous call.
My question then is two-fold:
Why can't the compiler resolve this ambiguity (general interest)?
What can I do to make this work, without having to write Derived().Base<int, char>::foo<int>();? EDIT: GuyGreer showed me that the call is disambiguated when I add two using-declarations. However, since I'm providing the base-class for the user to inherit from, this isn't an ideal solution. If at all possible, I don't want my users to have to add those declarations (which can be quite verbose and repetitive for large type-lists) to their derived classes.
Implementation of Contains:
template <typename T, typename ... Pack>
struct Contains;
template <typename T>
struct Contains<T>: public std::false_type
{};
template <typename T, typename ... Pack>
struct Contains<T, T, Pack ...>: public std::true_type
{};
template <typename T, typename U, typename ... Pack>
struct Contains<T, U, Pack ...>: public Contains<T, Pack...>
{};

Here's a simpler example:
template <typename T>
class Base2 {
public:
void foo(T ) { }
};
struct Derived: public Base2<int>,
public Base2<double>
{};
int main()
{
Derived().foo(0); // error
}
The reason for that comes from the merge rules [class.member.lookup]:
Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f,C) is
initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi,
and merge each such lookup set S(f,Bi) in turn into S(f,C).
— [..]
— Otherwise, if the declaration sets of S(f,Bi) and S(f,C) differ, the merge is ambiguous...
Since our initial declaration set is empty (Derived has no methods in it), we have to merge from all of our bases - but our bases have differing sets, so the merge fails. However, that rule explicitly only applies if the declaration set of C (Derived) is empty. So to avoid it, we make it non-empty:
struct Derived: public Base2<int>,
public Base2<double>
{
using Base2<int>::foo;
using Base2<double>::foo;
};
That works because the rule for applying using is
In the declaration set, using-declarations are replaced by the set
of designated members that are not hidden or overridden by members of the derived class (7.3.3),
There's no comment there about whether or not the members differ - we effectively just provide Derived with two overloads on foo, bypassing the member name lookup merge rules.
Now, Derived().foo(0) unambiguously calls Base2<int>::foo(int ).
Alternatively to having a using for each base explicitly, you could write a collector to do them all:
template <typename... Bases>
struct BaseCollector;
template <typename Base>
struct BaseCollector<Base> : Base
{
using Base::foo;
};
template <typename Base, typename... Bases>
struct BaseCollector<Base, Bases...> : Base, BaseCollector<Bases...>
{
using Base::foo;
using BaseCollector<Bases...>::foo;
};
struct Derived : BaseCollector<Base2<int>, Base2<std::string>>
{ };
int main() {
Derived().foo(0); // OK
Derived().foo(std::string("Hello")); // OK
}
In C++17, you can pack expand using declarations also, which means that this can be simplified into:
template <typename... Bases>
struct BaseCollector : Bases...
{
using Bases::foo...;
};
This isn't just shorter to write, it's also more efficient to compile. Win-win.

Though I can't tell you in detail why it doesn't work as is, I added using Base<int, char>::foo; and using Base<double, void>::foo; to Derived and it compiles fine now.
Tested with clang-3.4 and gcc-4.9

Related

C++ using declaration for parameter pack

I would like to define a class which inherits from a bunch of classes but which does not hide some specific methods from those classes.
Imagine the following code:
template<typename... Bases>
class SomeClass : public Bases...
{
public:
using Bases::DoSomething...;
void DoSomething(){
//this is just another overload
}
};
The problem is now if just one class does not have a member with the name DoSomething I get an error.
What I already tried was emulating an "ignore-if-not-defined-using" with a macro and SFINAE but to handle all cases this becomes very big and ugly!
Do you have any idea to solve this?
It would be really nice if I could define: "Hey using - ignore missing members".
Here I have some sample code: Godbolt
The problem with Jarod42's approach is that you change what overload resolution looks like - once you make everything a template, then everything is an exact match and you can no longer differentiate between multiple viable candidates:
struct A { void DoSomething(int); };
struct B { void DoSomething(double); };
SomeClass<A, B>().DoSomething(42); // error ambiguous
The only way to preserve overload resolution is to use inheritance.
The key there is to finish what ecatmur started. But what does HasDoSomething look like? The approach in the link only works if there is a single, non-overloaded, non-template. But we can do better. We can use the same mechanism to detect if DoSomething exists that is the one that requires the using to begin with: names from different scopes don't overload.
So, we introduce a new base class which has a DoSomething that will never be for real chosen - and we do that by making our own explicit tag type that we're the only ones that will ever construct. For lack of a better name, I'll name it after my dog, who is a Westie:
struct westie_tag { explicit westie_tag() = default; };
inline constexpr westie_tag westie{};
template <typename T> struct Fallback { void DoSomething(westie_tag, ...); };
And make it variadic for good measure, just to make it least. But doesn't really matter. Now, if we introduce a new type, like:
template <typename T> struct Hybrid : Fallback<T>, T { };
Then we can invoke DoSomething() on the hybrid precisely when T does not have a DoSomething overload - of any kind. That's:
template <typename T, typename=void>
struct HasDoSomething : std::true_type { };
template <typename T>
struct HasDoSomething<T, std::void_t<decltype(std::declval<Hybrid<T>>().DoSomething(westie))>>
: std::false_type
{ };
Note that usually in these traits, the primary is false and the specialization is true - that's reversed here. The key difference between this answer and ecatmur's is that the fallback's overload must still be invocable somehow - and use that ability to check it - it's just that it's not going to be actually invocable for any type the user will actually use.
Checking this way allows us to correctly detect that:
struct C {
void DoSomething(int);
void DoSomething(int, int);
};
does indeed satisfy HasDoSomething.
And then we use the same method that ecatmur showed:
template <typename T>
using pick_base = std::conditional_t<
HasDoSomething<T>::value,
T,
Fallback<T>>;
template<typename... Bases>
class SomeClass : public Fallback<Bases>..., public Bases...
{
public:
using pick_base<Bases>::DoSomething...;
void DoSomething();
};
And this works regardless of what all the Bases's DoSomething overloads look like, and correctly performs overload resolution in the first case I mentioned.
Demo
How about conditionally using a fallback?
Create non-callable implementations of each method:
template<class>
struct Fallback {
template<class..., class> void DoSomething();
};
Inherit from Fallback once for each base class:
class SomeClass : private Fallback<Bases>..., public Bases...
Then pull in each method conditionally either from the base class or its respective fallback:
using std::conditional_t<HasDoSomething<Bases>::value, Bases, Fallback<Bases>>::DoSomething...;
Example.
You might add wrapper which handles basic cases by forwarding instead of using:
template <typename T>
struct Wrapper : T
{
template <typename ... Ts, typename Base = T>
auto DoSomething(Ts&&... args) const
-> decltype(Base::DoSomething(std::forward<Ts>(args)...))
{
return Base::DoSomething(std::forward<Ts>(args)...);
}
template <typename ... Ts, typename Base = T>
auto DoSomething(Ts&&... args)
-> decltype(Base::DoSomething(std::forward<Ts>(args)...))
{
return Base::DoSomething(std::forward<Ts>(args)...);
}
// You might fix missing noexcept specification
// You might add missing combination volatile/reference/C-elipsis version.
// And also special template versions with non deducible template parameter...
};
template <typename... Bases>
class SomeClass : public Wrapper<Bases>...
{
public:
using Wrapper<Bases>::DoSomething...; // All wrappers have those methods,
// even if SFINAEd
void DoSomething(){ /*..*/ }
};
Demo
As Barry noted, there are other drawbacks as overload resolution has changed, making some call ambiguous...
Note: I proposed that solution as I didn't know how to create a correct traits to detect DoSomething presence in all cases (overloads are mainly the problem).
Barry solved that, so you have better alternative.
You can implement this without extra base classes so long as you’re willing to use an alias template to name your class. The trick is to separate the template arguments into two packs based on a predicate:
#include<type_traits>
template<class,class> struct cons; // not defined
template<class ...TT> struct pack; // not defined
namespace detail {
template<template<class> class,class,class,class>
struct sift;
template<template<class> class P,class ...TT,class ...FF>
struct sift<P,pack<>,pack<TT...>,pack<FF...>>
{using type=cons<pack<TT...>,pack<FF...>>;};
template<template<class> class P,class I,class ...II,
class ...TT,class ...FF>
struct sift<P,pack<I,II...>,pack<TT...>,pack<FF...>> :
sift<P,pack<II...>,
std::conditional_t<P<I>::value,pack<TT...,I>,pack<TT...>>,
std::conditional_t<P<I>::value,pack<FF...>,pack<FF...,I>>> {};
template<class,class=void> struct has_something : std::false_type {};
template<class T>
struct has_something<T,decltype(void(&T::DoSomething))> :
std::true_type {};
}
template<template<class> class P,class ...TT>
using sift_t=typename detail::sift<P,pack<TT...>,pack<>,pack<>>::type;
Then decompose the result and inherit from the individual classes:
template<class> struct C;
template<class ...MM,class ...OO> // have Method, Others
struct C<cons<pack<MM...>,pack<OO...>>> : MM...,OO... {
using MM::DoSomething...;
void DoSomething();
};
template<class T> using has_something=detail::has_something<T>;
template<class ...TT> using C_for=C<sift_t<has_something,TT...>>;
Note that the has_something here supports only non-overloaded methods (per base class) for simplicity; see Barry’s answer for the generalization of that.

Variadic template only compiles when forward declared

I have a variadic template that inherits from all template arguments:
template <typename... Ts>
struct derived : Ts...
{
};
I would also like to have a facility for expressing the type of "existing derived with added template arguments". My attempt at this is:
// Do not ODR-use (goes in namespace impl or similar)!
template<class ... NewInputs, class ... ExistingInputs>
auto addedHelper(const derived<ExistingInputs...>&)
-> derived<ExistingInputs..., NewInputs...>;
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
As a simple example, Added<derived<A, B>, C> should be derived<A, B, C>. I use the helper function for template argument deduction of the first parameter pack.
My problem: For some reason, I can use this successfully with incomplete types if derived has been forward declared, but not if it was defined.
Why does this code not compile:
#include <utility>
template <typename... Ts>
struct derived : Ts...
{};
template<class ... NewInputs, class ... ExistingInputs>
auto addedHelper(const derived<ExistingInputs...>&)
-> derived<ExistingInputs..., NewInputs...>;
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
struct A;
struct B;
struct C;
// Goal: This forward declaration should work (with incomplete A, B, C).
auto test(derived<A, B> in) -> Added<decltype(in), C>;
struct A {};
struct B {};
struct C {};
void foo()
{
auto abc = test({});
static_assert(std::is_same_v<decltype(abc), derived<A, B, C>>, "Pass");
}
Whereas this code does compile:
#include <utility>
template <typename... Ts>
struct derived;
template<class ... NewInputs, class ... ExistingInputs>
auto addedHelper(const derived<ExistingInputs...>&)
-> derived<ExistingInputs..., NewInputs...>;
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
struct A;
struct B;
struct C;
// Goal: This forward declaration should work (with incomplete A, B, C).
auto test(derived<A, B> in) -> Added<decltype(in), C>;
template <typename... Ts>
struct derived : Ts...
{};
struct A {};
struct B {};
struct C {};
void foo()
{
auto abc = test({});
static_assert(std::is_same_v<decltype(abc), derived<A, B, C>>, "Pass");
}
For convenience, here are both cases at once (comment in/out #define FORWARD_DECLARED): https://godbolt.org/z/7gM52j
I do not understand how code could possibly become illegal by replacing a forward declaration by the respective definition (which would otherwise just come later).
Evg's observation hits the nail on the head: the problem here is ADL. It's actually the same problem I ran into with this question.
The issue is this: we have an unqualified call here:
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
// ^^^^^^^^^^^
We know it's a function template because we find it in using regular lookup, so we don't have to deal with the whole "is < an operator or a template introducer" question. However, because it's an unqualified call, we also must perform argument-dependent lookup.
ADL needs to look into the associated namespaces of all the arguments, which seems fine - we don't need complete types for that. But ADL also needs to look for potential friend functions and function templates defined within the classes. After all, this needs to work:
struct X {
friend void foo(X) { }
};
foo(X{}); // must work, call the hidden friend defined within X
As a result, in our call in question:
auto test(derived<A, B> in) -> Added<decltype(in), C>;
We have to instantiate derived<A, B>... but that type inherits from two incomplete classes, which we can't do. That's where the problem is, that's where we fail.
This is why the forward declaration version works. template <typename... T> struct derived; is incomplete, so just trying to look inside of it for friend functions trivially finds nothing - we don't need to instantiate anything else.
Likewise, a version where derived was complete but didn't actually derive from anything would also work.
Thankfully, this is fixable in this context with what Evg suggested. Make a qualified call:
template<class ExistingInput, class ... NewInputs>
using Added = decltype(::addedHelper<NewInputs...>(std::declval<ExistingInput>()));
This avoids ADL, which you didn't even want. Best case, you're avoiding doing something that has no benefit to you. Bad case, your code doesn't compile. Evil case, for some inputs you accidentally call a different function entirely.
Or just use Boost.Mp11's mp_push_back

How to restrict a method template parameter to types which appear in the template arguments of enclosing structure? [duplicate]

Suppose I have this variadic base class-template:
template <typename ... Types>
class Base
{
public:
// The member foo() can only be called when its template
// parameter is contained within the Types ... pack.
template <typename T>
typename std::enable_if<Contains<T, Types ...>::value>::type
foo() {
std::cout << "Base::foo()\n";
}
};
The foo() member can only be called when its template-parameter matches at least one of the parameters of Base (the implementation of Contains is listed at the bottom at this post):
Base<int, char>().foo<int>(); // fine
Base<int, char>().foo<void>(); // error
Now I define a derived class that inherits twice from Base, using non-overlapping sets of types:
struct Derived: public Base<int, char>,
public Base<double, void>
{};
I was hoping that when calling e.g.
Derived().foo<int>();
the compiler would figure out which base-class to use, because it is SFINAE'd out of the one that does not contain int. However, both GCC 4.9 and Clang 3.5 complain about an ambiguous call.
My question then is two-fold:
Why can't the compiler resolve this ambiguity (general interest)?
What can I do to make this work, without having to write Derived().Base<int, char>::foo<int>();? EDIT: GuyGreer showed me that the call is disambiguated when I add two using-declarations. However, since I'm providing the base-class for the user to inherit from, this isn't an ideal solution. If at all possible, I don't want my users to have to add those declarations (which can be quite verbose and repetitive for large type-lists) to their derived classes.
Implementation of Contains:
template <typename T, typename ... Pack>
struct Contains;
template <typename T>
struct Contains<T>: public std::false_type
{};
template <typename T, typename ... Pack>
struct Contains<T, T, Pack ...>: public std::true_type
{};
template <typename T, typename U, typename ... Pack>
struct Contains<T, U, Pack ...>: public Contains<T, Pack...>
{};
Here's a simpler example:
template <typename T>
class Base2 {
public:
void foo(T ) { }
};
struct Derived: public Base2<int>,
public Base2<double>
{};
int main()
{
Derived().foo(0); // error
}
The reason for that comes from the merge rules [class.member.lookup]:
Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f,C) is
initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi,
and merge each such lookup set S(f,Bi) in turn into S(f,C).
— [..]
— Otherwise, if the declaration sets of S(f,Bi) and S(f,C) differ, the merge is ambiguous...
Since our initial declaration set is empty (Derived has no methods in it), we have to merge from all of our bases - but our bases have differing sets, so the merge fails. However, that rule explicitly only applies if the declaration set of C (Derived) is empty. So to avoid it, we make it non-empty:
struct Derived: public Base2<int>,
public Base2<double>
{
using Base2<int>::foo;
using Base2<double>::foo;
};
That works because the rule for applying using is
In the declaration set, using-declarations are replaced by the set
of designated members that are not hidden or overridden by members of the derived class (7.3.3),
There's no comment there about whether or not the members differ - we effectively just provide Derived with two overloads on foo, bypassing the member name lookup merge rules.
Now, Derived().foo(0) unambiguously calls Base2<int>::foo(int ).
Alternatively to having a using for each base explicitly, you could write a collector to do them all:
template <typename... Bases>
struct BaseCollector;
template <typename Base>
struct BaseCollector<Base> : Base
{
using Base::foo;
};
template <typename Base, typename... Bases>
struct BaseCollector<Base, Bases...> : Base, BaseCollector<Bases...>
{
using Base::foo;
using BaseCollector<Bases...>::foo;
};
struct Derived : BaseCollector<Base2<int>, Base2<std::string>>
{ };
int main() {
Derived().foo(0); // OK
Derived().foo(std::string("Hello")); // OK
}
In C++17, you can pack expand using declarations also, which means that this can be simplified into:
template <typename... Bases>
struct BaseCollector : Bases...
{
using Bases::foo...;
};
This isn't just shorter to write, it's also more efficient to compile. Win-win.
Though I can't tell you in detail why it doesn't work as is, I added using Base<int, char>::foo; and using Base<double, void>::foo; to Derived and it compiles fine now.
Tested with clang-3.4 and gcc-4.9

Enable if/else class member template instantiation

Can anyone tell my how to enable if/else class member template based on different derived classes from pre-defined base set? Let me use the following example:
enum class Type {
TYPEA,
TYPEB
};
// Predefined in libraries.
class BaseA {...};
class BaseB {...};
class Foo {
template <typename Derived, Type type>
void foo();
};
// User-derived
class DerivedA : public BaseA {};
class DerivedB : public BaseB {};
Normally we need two template typenames for calling the member foo.
Foo obj;
obj.foo<DerivedA, Type::TypeA>()
obj.foo<DerivedB, Type::TypeB>();
However, this native approach seems lengthy because the second template argument Type::TypeA and Type::TypeB can obviously be deduced by compiler through the first argument DerivedA and DerivedB, if they are derived from pre-defined base properly. I notice that c++11 provides is_base_of template but I am not sure how to use it in my case. To be more specific, below is the expected solution:
obj.foo<DerivedA>(); // Automatically deduce type = Type::TypeA
obj.foo<DerivedB>(); // Automatically deduce type = Type::TypeB
And if the compile fails to deduce the Type from the first typename, it should it just goes back to the normal declaration obj.foo<MyClass, MyType> where MyType is either Type::TypeA or Type::TypeB.
Sounds like you just want a default template argument:
class Foo {
template <typename Derived, Type type = get_type_from<Derived>::value>
void foo();
};
Where get_type_from<> is a metafunction to be filled in later based on how you actually figure out the Types.
template<Type t>
using etype_tag = std::integral_constant<Type, t>;
template<class T>
struct tag_t {
using type=T;
template<class D,
std::enable_if_t<std::is_base_of<T, D>::value, int>* =nullptr
>
constexpr tag_t( tag_t<D> ) {}
constexpr tag_t() = default;
constexpr tag_t(tag_t const&) = default;
};
template<class T>
constexpr tag_t<T> tag{};
constexpr etype_tag<Type::TYPEA> get_etype( tag_t<BaseA> ) { return {}; }
constexpr etype_tag<Type::TYPEB> get_etype( tag_t<BaseB> ) { return {}; }
template<class T>
constexpr decltype( get_etype( tag<T> ) ) etype{};
Now etype<Bob> is a compile-time constant integral constant you want.
class Foo {
template <typename Derived, Type type=etype<Derived>>
void foo();
};
makes the 2nd argument (usually) redundant.
You can extend get_etype with more overloads in either the namespace where etype is declared, or in the namespace of tag_t, or in the namespace of the type you are extending get_etype to work with, and etype will automatically gain support (assuming it is used in a context where the extension is visible: failure of that requirement leaves your program ill formed).
Live example

Can I make a template specialisation that inherits from its base?

In other words: Is it possible to make a template specialisation that inherits from its base, like this:
template <class T>
class A{};
template <>
class A<int>:public A<>{};
so that A has all of A's functions?
I'm new here, so I dunno how to format, just in case the code comes up incorrectly.
You can, with a bit of trickery. This pattern is sometimes called “template subclassing” and is used extensively in the SeqAn library.
The trick is to give the base class an additional template argument tag which determines the type identity:
template <typename T, typename Spec = void>
struct A { … };
// inheritance tag:
struct Derived { };
template <typename T>
struct A<T, Derived> : public A<T, void> { … };
Here, void denotes the base (you could also use a dedicated tag Base but void works fine) and Derived, an empty struct, denotes the derived class.
Now you can instantiate and use the templates as follows:
A<int> the_base;
A<int, Derived> the_derived;
the_base.do_something();
the_derived.do_something();
For a real-world example, consider the String class from SeqAn:
String<Dna> some_dna = "GATTACA";
String<Dna, Packed> more_dna = "GATTACA";
The second type derived from the first one, but is a specialisation which packs its characters as tightly as possible (for DNA, this means putting four DNA characters in each byte).