Reputation: 1157
bar
defined below allows two kinds of initalizations(the template of bar
will always be several int
s in use)
template<class C>
inline void store(C dst) {}
template<class T, class C, class... E>
inline void store(C dst, T val, E... etc) {
*dst = val;
store(++dst, etc...);
}
template<class... T>
class bar {
static const int n = sizeof...(T);
int data[n];
public:
bar(int x) {for(int i=0; i<n; i++) data[i]=x;}
bar(T... x) {store(data,x...);}
};
It looks fine; however if the template is exactly one int
, this code is ambigious to compiler(though both understanding have same meaning):
bar<int> s(3);
Is the only way to avoid this to specialization the one int
case? (That anyway makes the code more complex)
Upvotes: 1
Views: 97
Reputation: 218278
Is the only way to avoid this to specialization the one int case?
No as shown with other answer with SFINAE.
C++20 would even allow nicer syntax with requires
:
template <class... Ts>
class bar {
static const int n = sizeof...(Ts);
int data[n];
public:
bar(int x) { std::fill(std::begin(data), std::end(data), x);}
bar(Ts... xs) requires (n != 1) : data{xs...} {}
};
(That anyway makes the code more complex)
Not really agree compared to SFINAE, with specialization, it might be:
template <class... Ts>
class bar_impl
{
protected:
static const int n = sizeof...(Ts);
int data[n];
public:
bar(Ts... xs) : data{xs...} {}
};
template <>
class bar_impl<int> {
static const int n = 1;
int data[n];
};
template <class... Ts>
class bar : bar_impl<Ts...> {
public:
using bar_impl<Ts...>::bar_impl;
bar(int x) { std::fill(std::begin(data), std::end(data), x);}
// ...
};
Upvotes: 0
Reputation: 11250
You can disable the variadic constructor when there's just one argument and is an int
.
If you have c++17 you can do it like
template <
std::size_t N = sizeof...(T),
std::enable_if_t<(N != 1 || !(std::is_same_v<T, int> && ...)), bool> = true>
bar(T... x) {store(data,x...);}
Otherwise, you can go along with:
template <bool... Pred>
struct all_dummy;
template <bool... Preds>
using all = std::is_same<all_dummy<Preds...>, all_dummy<((void)Preds, true)...>>;
template <
std::size_t N = sizeof...(T),
std::enable_if_t<(N != 1 || !all<std::is_same<T, int>::value...>::value), bool> = true
>
bar(T... x) {store(data,x...);}
Upvotes: 1
Reputation: 66230
What about transforming the second constructor in a template one and SFINAE enable it only when T...
isn't int
?
I mean something as
template <std::size_t N = sizeof...(T),
typename = std::enable_if_t<
(N != 1u)
|| (false == std::is_same<std::tuple<int, T...>,
std::tuple<T..., int>>{})>>
bar(T... x) {store(data,x...);}
Obviously, if you can use only C++11, you have to use typename std::enable_if<>::type
instead of std::enable_if_t<>
.
If you can use C++17, you can use template folding, to check that T...
isn't int
, as suggested by Jans.
Upvotes: 1