I am trying to create thing similar to tuple, but I have come across a problem for writing my constructor.
Here is the code:
#include <tuple>
template <typename... Ts>
struct B {
template <typename... ArgTypes>
explicit B(ArgTypes&&... args)
{
static_assert(sizeof...(Ts) == sizeof...(ArgTypes),
"Number of arguments does not match.");
}
};
struct MyType {
MyType() = delete;
MyType(int x, const char* y) {}
};
int main()
{
B <int, char> a{2, 'c'}; // works
B <int, bool, MyType, char> b{2, false, {4, "blub"}, 'c'}; // fails
std::tuple<int, bool, MyType, char> t{2, false, {4, "blub"}, 'c'}; // works
}
Now, this works ok if pass simple types as initializers, but it does not, if I try to pass arguments in a brace-enclosed initializer list for non-trivial object.
GCC-4.7 emits the following:
vararg_constr.cpp:21:67: error: no matching function for call to 'B<int, bool, MyType, char>::B(<brace-enclosed initializer list>)'
vararg_constr.cpp:21:67: note: candidates are:
vararg_constr.cpp:6:14: note: B<Ts>::B(ArgTypes&& ...) [with ArgTypes = {}; Ts = {int, bool, MyType, char}]
vararg_constr.cpp:6:14: note: candidate expects 0 arguments, 4 provided
Clang-3.1 the following:
vararg_constr.cpp:21:40: error: no matching constructor for initialization of
'B<int, bool, MyType, char>'
B <int, bool, MyType, char> b{2, false,{4, "blub"}, 'c'}; // fails
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
vararg_constr.cpp:6:14: note: candidate constructor not viable: requires 2
arguments, but 4 were provided
explicit B(ArgTypes&&... args)
Ok, now what makes me very, very curious is that it works for tuple! According to the Standard (20.4.2.1) it has a constructor, that looks pretty much like mine.
template <class... Types>
class tuple {
public:
// ...
template <class... UTypes>
explicit tuple(UTypes&&...);
// ...
};
When constructing the tuple object in the same way, it works!
Now I would like to know:
A) What the hell? Why is std::tuple so special, and why don't compilers deduce the correct number of arguments?
B) How can I make this work?
A) Why should the Compiler know, that {4, "blub"}
is of type MyType and not tuple<int, const char*>
?
B) Change ArgTypes to Ts in the constructor:
explicit B(Ts&&... args)
Tuple does also have the following constructor:
explicit constexpr tuple(const _Elements&... __elements);
EDIT: The point is, that the constructor with const& is called and not that with the R-Values. Consider the following:
template <typename... Ts>
struct B {
explicit B(const Ts&... elements) { std::cout << "A\n"; }
template<typename... As,
typename = typename std::enable_if<sizeof...(As) == sizeof...(Ts)>::type>
explicit B(As&&... elements) { std::cout << "B\n" ;}
};
int main()
{
MyType m {1, "blub"};
B<int, char> a{2, 'c'}; // prints B
B<bool, MyType, char> b{false, {4, "blub"}, 'c'}; // prints A
B<bool, MyType, MyType>c{false, {4, "blub"}, std::move(m)}; // prints A
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With