The following is a simplified, contrived example of an issue that I'm facing. Essentially, I need an object that can hold an arbitrary number of items, and return those items when needed.
template<typename... Ts>
class Foo {
public:
Foo(Ts... args) :
mArgs{std::forward<Ts>(args)} {
}
std::tuple<Ts...> getArgs() const {
return mArgs;
}
private:
std::tuple<Ts...> mArgs;
};
This works fine when args > 1.
Foo<int, int> f{1, 2};
auto result = f.getArgs(); // result is a tuple containing two ints.
However, if args == 1, I would prefer to NOT get a tuple from getArgs
.
Foo<int> f{1};
auto result = f.getArgs(); // result is a tuple, but I want it to be an int.
Is there a way, perhaps using SFINAE, to define another getArgs
function that is used when args == 1? Something like (and this is obviously very wrong):
template<typename = std::enable_if_t<std::tuple_size_v<Ts...> == 1>>
??? getArgs() const {
return mArgs;
}
Several obvious problems with this:
std::tuple_size_v<Ts...>
will even work.std::tuple_element
would be useful here.A different approach: Use sizeof...(T) == 1
to determine whether only a single type is in use. Then use the auto
return type and if constexpr
to do the rest.
template<typename... T>
class Foo {
public:
Foo(T&&... t)
: mArgs{std::forward<T>(t)...} {
}
auto getArgs() const {
if constexpr (sizeof...(T) == 1) {
return std::get<0>(mArgs);
} else {
return mArgs;
}
}
private:
std::tuple<T...> mArgs;
};
Example usage:
auto foo_i = Foo{11};
auto foo_c = Foo{'c'};
auto foo_ic = Foo{11, 'c'};
int i2 = foo_i.getArgs();
char c2 = foo_c.getArgs();
std::tuple<int, char> ic2 = foo_ic.getArgs();