I would like to enforce the type of variadic template to be identical to an earlier set template type. In the below example, I'd like T and U to be the same type.
code on ideone.com
#include <iostream>
#include <string>
template<class T>
struct Foo {
Foo(T val) {
std::cout << "Called single argument ctor" << std::endl;
// [...]
}
// How to enforce U to be the same type as T?
template<class... U>
Foo(T first, U... vals) {
std::cout << "Called multiple argument ctor" << std::endl;
// [...]
}
};
int main() {
// Should work as expected.
Foo<int> single(1);
// Should work as expected.
Foo<int> multiple(1, 2, 3, 4, 5);
// Should't work (but works right now). The strings are not integers.
Foo<int> mixedtype(1, "a", "b", "c");
// Also shouldn't work. (doesn't work right now, so that is good)
Foo<int> alsomixedtype(1, 1, "b", "c");
}
We can use SFINAE to ensure that all U
types are the same as T
. An important thing to note is that U
is not just one type as you imply, but a list of possibly disparate types.
template<class... U, std::enable_if_t<all_same<T, U...>::value>* = nullptr>
Foo(T first, U... vals) {
std::cout << "Called multiple argument ctor" << std::endl;
// [...]
}
std::enable_if_t
is from C++14. If that's not an option for you, just use std::enable_if
.
typename std::enable_if<all_same<T, U...>::value>::type* = nullptr>
all_same
can be implemented in a bunch of different ways. Here's a method I like using boolean packs:
namespace detail
{
template<bool...> struct bool_pack;
template<bool... bs>
//if any are false, they'll be shifted in the second version, so types won't match
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
}
template <typename... Ts>
using all_true = detail::all_true<Ts::value...>;
template <typename T, typename... Ts>
using all_same = all_true<std::is_same<T,Ts>...>;
std::conjunction
(logical AND) was introduced in C++17 so one doesn't have to implement all_same
manually anymore. Then the constructor becomes simply:
template<typename... U,
typename = std::enable_if_t<
std::conjunction_v<
std::is_same<T, U>...
>
>
>
Foo(T first, U... vals)
{
std::cout << "Called multiple argument ctor" << std::endl;
// [...]
}
See live example.
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