std::variant of template specializations converting move constructor - c++

I am using a class that encapsulates an std::variant of template specializations, such as:
template<typename Type> struct Generic_node {...};
struct Leaf_node : Generic_node<...> {...};
struct Inner_node : Generic_node<...> {...};
struct Node {std::variant<Leaf_node, Inner_node> variant_;};
I am trying to construct a Node from a function in Generic_node using a converting move constructor, but the compilation fails.
I defined a template constructor, which accepts an rvalue reference (assuming one of the specialized classes) and constructs the variant by moving the value to the variant, where I expect to call the converting move constructor # (4).
When i was trying to create minimal non-working example, i found that the problem really shows only from the template function, where if I knew the exact type (Leaf_node == Generic_node<...> which the compiler knows), the move construction would succeed. Therefore I assume there is as always some template magic happening that I did not anticipate.
#include <variant>
template<typename T>
struct Base
{
void f();
};
struct Derived : Base<int> {};
struct Variant
{
// In real program expecting the T to be one of the multiple variants
// Here I use only one variant because it suffices to illustrate the problem
template<typename T>
Variant(T&& t)
:
variant_ {std::move(t)}
{
}
std::variant<Derived> variant_;
};
template<typename T>
void
Base<T>::
f()
{
Variant {std::move(Derived {})}; // can call on exact type
Variant {std::move(Base<T> {})}; // can not call on specialized template type
}
int
main()
{
Derived {}.f();
}
Relevant compiler error message (clang 7, libstdc++-8):
note: candidate template ignored: substitution failure [with _Tp = Base<int>,
$1 = void, $2 = void]: implicit instantiation of undefined template
'std::variant<Derived>::__to_type_impl<18446744073709551615, false>'
variant(_Tp&& __t)
The problem most probably does not have anything to do with variants, but with the equality of Base<T> == Derived in the template instantiation of the Variant constructor, which the compiler as if did not see.
What is happening in the template instantiations, why can't the compiler call the provided constructor?
Edit: Since I intended to create a specialization, I forgot that inheritance can not imply class type equality, even if it technically is in this special case. This is therefore an easy task of constructing Derived by move from the specialized Base:
struct Derived : Base<int>
{
Derived() = default;
Derived(Base<int>&&) {}
};
If I am correct, the constructor needs to be explicitly defined for each derived class of Base.

In the example you are giving, the Derived class is a separate class from Base. They have exactly the same members, same methods, but they are still separate classes.
Easiest way to solve it would be to use a using statement instead of declaring it as a separate class:
using Derived = Base<int>;

Related

How to pass a templated function parameter with a subclass of the templated type in C++?

Compiling the following contrived example:
class Base
{};
class Derived : public Base
{};
template< typename T >
class A
{};
class B
{
public:
static void f( const A< Base >& ) {}
};
int main()
{
A< Base > tb;
A< Derived > td;
B::f( tb );
B::f( td );
return 0;
}
using g++-8 gives me the following error:
error: no matching function for call to 'B::f(A<Derived>&)'
B::f( td );
note: no known conversion for argument 1 from 'A<Derived>' to 'const A<Base>&'
Why?
Since Derived is-a Base and it doesn't override any of Base's stuff, why can't I give a Derived in the place of a Base in the templated function parameter?
It is true that Derived is derived from Base, but that doesn't mean that A<Derived> must therefore be derived from A<Base>. C++ templates don't work this way.
All that A<Derived> is, is a class, instantiated by the A template. You could've simply declared:
class A_Derived {
// ...
};
With the same members (if it had any), and pretty much got the same results. Same for A<Base>. With nothing else in the picture, the two classes have absolutely nothing to do with each other, whatsoever. You can draw a mental picture here, as if you made the following declarations:
class A_Derived {
};
and
class A_Base {
};
Which is pretty much what this is history. Do you see A_Derived being explicitly derived from A_Base here? Obviously not. If something expects a reference or a pointer to A_Base, you cannot give it A_Derived, because the two classes have absolutely nothing to do with each other. They are independent classes.
P.S. You could declare an explicit specialization of A<Derived> as being derived from A<Base>, if you so wish, but specialization is a completely different topic...
Template instances, like A<Base> and A<Derived>, are different types. In particular they do not have any inheritance relationship even if Base and Derived do.
There are quite a few ways you can make what you want work.
First, you could make A<Derived> explicitly derive from A<Base>, but that means adding a whole class definition.
template<>
class A<Derived> : public A<Base>
{};
Second, you can provide an implicit conversion from A<Derived> to A<Base> in the form of a constructor template. You can use std::enable_if and std::is_base_of to only allow A<T> where T is derived from Base, or directly std::is_same if you only want to consider this particular Derived type.
template<typename T>
class A
{
template<typename U, typename = std::enable_if_t<std::is_base_of_v<T, U>>>
A(A<U> const& other);
};
Third, you can provide an implicit conversion in the form of an operator template, in much the same way.
template<typename T>
class A
{
template<typename U, typename = std::enable_if_t<std::is_base_of_v<U, T>>>
operator U();
};
Fourth, you can make f a function template and restrict what types it takes.
template<typename T, typename = std::enable_if_t<std::is_base_of_v<Base, T>>>
static void f(A<T> const& a);

How to overload a method of base class passed as a parameter to a template class in C++?

There are two classes:
class A {
public:
virtual void foo( int bar );
}
class B {
virtual void foo( string bar, int baz);
}
Now, the class(es) I'm building can derive from either class. But there's some common helper code, so I want to factor it out into a base class.
This common code must be called from foo and should take same arguments as the corresponding foo method. So I declare this template class, but don't know, whether it is possible to "extract" foo's signature from the template argument (which is a base class -- either A or B):
template<class Base>
class CommonBase : public Base {
public:
// how do I overload Base::foo here?
void foo(/*Base::foo arguments here*/) {
commonCode(/*Base::foo arguments here*/);
}
protected:
// how do I define commonCode with Base::foo signature below?
void commonCode(/*Base::foo arguments here*/) { ... }
}
I have little experience with C++ templates, so wondering -- is it even possible?
One solution I see is to add another template parameter for method signature and pass it explicitly when specializing. But it feels redundant as the knowledge of foo signature will be already contained in the Base class parameter (and compilation should fail if Base does not provide foo at all).
One solution I see is to add another template parameter for method signature and pass it explicitly when specializing.
This is on the right track, but you don't have to pass it explicitly; you can extract the type from the base class:
template<class Base, class... Arg>
class CommonBaseImpl : public Base {
public:
// how do I overload Base::foo here?
void foo(Arg... arg) override {
commonCode(std::forward<Arg>(arg)...);
}
protected:
// how do I define commonCode with Base::foo signature below?
void commonCode(Arg... arg) { ... }
};
template <class Base, class Foo = decltype(&Base::foo)>
struct BaseSelector;
template <class Base, class... Arg>
struct BaseSelector<Base, void (Base::*)(Arg...)>
{
using type = CommonBaseImpl<Base, Arg...>;
};
template <class Base>
using CommonBase = typename BaseSelector<Base>::type;
[Live example]
This works by using class template partial specialisation to decompose the function type. The template parameter Foo of BaseSelector will hold the type of member pointer to foo. To get this type, we use decltype(&Base::foo), the default argument for that parameter.
However, we need to access the individual argument types from within that type. This is normally done using template partial specialisation, as here. Basically, the primary template says: "This class template takes two types, Base and Foo." They're types and we know nothing more about them. We also don't use them for anything (the primary template is not even defined).
Then, we provide a specialisation. That effectively says: "When the type Foo happens to be a pointer to member function of Base which returns void and takes arguments of type Arg..., then do this: { partially specialised class definition }". In practice, it's just a way to assign names to the various components of the pointer-to-member type.

Template argument deduction for class templates in C++17: am I doing it wrong?

According to
https://gcc.gnu.org/projects/cxx-status.html, version 7 of g++, used with flag -std=c++1z, supports template argument deduction for class templates.
I would expect the following code to compile, especially as Base is an abstract class, therefore:
1. the compiler knows no instance of Base can be created;
2. the pointer to base pt_base points to a clearly defined instance (i.e. Derived<int>{42}) where the type (int) is explicit.
template<typename ValueType>
class Base {
public:
virtual ValueType getValue() = 0;
};
template<typename ValueType>
class Derived : public Base<ValueType>{
public:
Derived(ValueType argt){ value = argt; }
virtual ValueType getValue(){ return value; }
ValueType value;
};
int main(){
Base *pt_base = new(Derived<int>{42}); // *ERROR*
delete pt_base;
}
Yet, it does not compile. G++ complains that "template placeholder type 'Base' must be followed by a simple declarator-id"; if I understand correctly, it does not deduce the template argument.
It's a pity because I would like to dynamically decide which derived class pt_base points to (could be an object from class Derived<someType> or from class Derived2<someType2>). That way, an array or a vector<Base *> could store pointers to objects of various derived classes.
GCC only has experimental support for C++17 and I don't have access to another compiler, so although I get a compile error I am not sure my code is wrong. What do you think?
And how could we dynamically decide that pt_base points to an object from either Derived<someType> or Derived2<someType2> (so polymorphism can be used)?
Class template argument deduction works for declaring instances of class types:
Derived d(42);
Or new-expressions:
auto p = new Derived(42);
Or function-style casts:
foo(Derived(42));
It does not work for declaring pointers.
You'll have to simply provide the template arguments as you've always had to. Or, I guess:
template <class T> Base<T>* downcast(Base<T>* p) { return p; }
auto pt_base = downcast(new Derived(42));

Ambiguous base class conversion with a compressed pair

So I tried creating a compressed pair using the empty base optimization. I would like it such that if class a and b are empty then compressed_pair<a, b> is empty as well. So I defined my compressed pair something like this:
template <class First, class Second>
struct compressed_pair : First, Second
{
compressed_pair() {}
compressed_pair(const First& x, const Second & y)
: First(x), Second(y)
{}
First& first() { return *this; }
Second& second() { return *this; }
};
However, if one of the types inherit from the other it becomes ambiguous. For example, when I compile this program:
struct a
{};
struct b : a
{};
int main()
{
compressed_pair<a, b> p;
auto x = p.first();
}
I get this error from clang:
compressed_pair.cpp:8:30: error: ambiguous conversion from derived class 'compressed_pair<a, b>' to base class 'a':
struct compressed_pair<struct a, struct b> -> struct a
struct compressed_pair<struct a, struct b> -> struct b -> struct a
First& first() { return *this; }
^~~~~
compressed_pair.cpp:21:16: note: in instantiation of member function 'compressed_pair<a, b>::first' requested here
auto x = p.first();
^
So how can I avoid the ambiguous conversion and still have compressed_pair<a, b> be empty?
The problem that you have is that from compressed_pair there are two bases to which the conversion can apply. You need to be able to drive the compiler into selecting one of them. The first thing that comes to mind is to add another layer of inheritance that you can use as a selector:
template <int N, typename T>
struct element : T {};
template <typename T, typename U>
struct compressed_pair : element<0, T>, element<1, U> { ... };
Then your implementation for the accessors could be:
template <typename T, typename U>
T& compressed_pair<T,U>::first() {
return static_cast<element<0,T>&>(*this);
}
The cast inside the accessor forces the selection of one of the two direct bases, element<0,T>. From that point onwards, there is only one base of type T.
You can also use move the accessor to that intermediate step, rather than have it in the complete type. Finally, through specialization you should be able to provide a different implementation for element that supports non-class types as your current implementation will choke on types like int.
Use virtual inheritance to avoid ambiguity or require that First and Second do not derived from each other. It's an either/or decision. You need to decide whether avoiding ambiguity or allowing the empty base optimisation is more important to you.
Generally speaking, to avoid ambiguity, the one that is inherited more than once (i.e. by both your compressed_pair and the other struct type) needs to be a virtual base. Since the template might be instantiated using anything, this means that both First and Second must be a virtual base of compressed_pair, and a must be a virtual base of b.
If you always know that Second might be derived from First but First will never be derived from Second you can relax this somewhat (with the cost of ambiguity again, if First is derived from Second).
Bear in mind that virtual inheritance does impose some constraints on how the classes work. For example, it affects order of construction of bases, requires any subsequently derived classes to manage construction of virtual bases inherited via base classes, etc.

Derive from template constructor of template base class

Just curious, is it ever possible to inherit from a template class and in constructor of the derived class, call constructor of the base class which is also templated and has no arguments to deduce its types from?
template<typename T>
struct Base {
template<typename D>
Base() { // no argument of type D to infer from
static_assert(std::is_same<T,D>::value, "");
}
};
struct Derived : Base<int> {
Derived() : Base<int>::Base<int>() {} // is there a way to write it correctly?
};
I can replace template constructor by a template method in my particular case, but still it is an interesting question about the language flexibility.
What the C++ standard says about this (section 14.8.1):
[ Note: Because the explicit template argument list follows the function template name, and because conversion member function templates and constructor member function templates are called without using a function name, there is no way to provide an explicit template argument list for these function templates. — end note ]
It's a note, not a rule, because it actually is a consequence of two other rules, one in the same section:
Template arguments can be specified when referring to a function template specialization by qualifying the function template name with the list of template-arguments in the same way as template-arguments are specified in uses of a class template specialization.
and from 12.1
Constructors do not have names.
The template arguments of constructor templates must be deduced from their arguments, it's not possible to explicitly specify template arguments for constructors.
As such, have Base take a dummy parameter that deduces the argument:
template <typename T>
struct dummy { }; // to prevent instantiation of T
template <typename T>
struct Base
{
template <typename D>
Base(dummy<D>)
{
static_assert(std::is_same<T, D>::value, "");
}
};
struct Derived : Base<int>
{
Derived() : Base<int>(dummy<int>{}) { }
};
By the way the question is formulated it looks going towards a nonsensical paranoia.
Just think for plain classes:
class Base
{
public:
Base() {}
};
class Derived: public Base
{
public:
Derived() //< default ctor
:Base //< this is the Base type
() //< this selects what ctor to call
{}
};
Note that you call :Base(), that resolve into Base::Base(), not :Base::Base()
Now, by templetizing Base::Base() you are in fact trying to admit that there can be many different default ctor (with ()) for Base. That's a nonsense respect ot the concept itself of "default".
Even if Base is not by itself a template, this is not possible:
class Base
{
public:
template<class T>
Base() {} //< are there infinite way to default construct Base ?!?
};
Base b; //< so how is b constructed ?
Thing gets only apparently different with varadics:
template<class T>
class Base
{
public:
template<class... S>
Base(S&&... s) { /* something to do */ }
};
class Derived: public Base<int>
{
public:
template<class... S>
Derived(S&&... s) //< Derived varadicly initialized
:Base //< base type ...
(std::forward<S>(s)...) //< ... to initialize with what
{}
};
Note that in case s is empty you are in fact calling Base::Base() from Derived()::Derived(), templetized with <> (no args)