我正在尝试创建一个可以包含类型包的类。
// Pack.hpp
template <typename... Types>
class Pack final
{
Pack(const std::tuple<Types...> items) : items_(std::move(items)) {};
std::tuple<Types...> items_;
};
但我希望
Pack
公开一些基于其内部 Types
的方法。例如,我想这样做:
// Consumer.cpp
Person person{ ... };
Car car{ ... };
Pack<Person, Car> personAndCarPack{ { person, car } };
Pack<Person> personPack{ { person } };
// Ok
personAndCarPack.getCar();
// Ok
personAndCarPack.getPerson();
// Ok
personPack.getPerson();
// Shouldn't compile - getCar() shouldn't exist!
personPack.getCar();
我假设我必须或多或少这样声明它:
// Pack.hpp
template <typename... Types>
class Pack final
{
Pack(const std::tuple<Types...> items) : items_(std::move(items)) {};
std::tuple<Types...> items_;
Car getCar()
{
return std::get<Car>(items_);
}
Person getPerson()
{
return std::get<Person>(items_);
}
};
但是当然,无论类型如何,都会公开
Pack
中的方法。
我得到的最接近的是:
typename std::enable_if<(std::is_same_v<Types, Car> && ...), Car>::type
getCar()
{
return std::get<Car>(items_);
}
但它无法编译;我明白了
'enable_if' cannot be used to disable this declaration
在
std::is_same_v()
通话中。
我可能误解了这一切是如何运作的。我找到了一些有些相关的答案,但没有一个答案能够根据我的需要正确组合因素。
这个功能有可能实现吗?如果是的话,怎么办?
这个功能有可能实现吗?如果是的话,怎么办?
是的,这是可能的。最小的改变ᕯ解决方案是没有
std::enable_if
/ SFINAE。
// Method to get a Car from the Pack (enabled only if Car is in Types...)
template <typename T = Car>
constexpr auto getCar() -> decltype(std::get<Car>(items_))
{
return std::get<Car>(items_);
}
// Method to get a Person from the Pack (enabled only if Person is in Types...)
template <typename T = Person>
constexpr auto getPerson() -> decltype(std::get<Person>(items_))
{
return std::get<Person>(items_);
}
ᕯ 请记住,上述内容不会禁止该函数在未评估的上下文中使用(即
decltype(personPack.getCar()) x = personAndCarPack.getCar();
仍然有效)。
或者,使用
std::enable_if_t
和 std::disjunction
,您也可以达到相同的效果。
// Method to get a Car from the Pack (enabled only if Car is in Types...)
template <typename T = Car>
constexpr auto getCar()
-> std::enable_if_t<std::disjunction<std::is_same<Types, T>...>::value, T>
{
static_assert(std::is_same_v<T, Car>, "T must be of type Car!");
return std::get<T>(items_);
}
// Method to get a Person from the Pack (enabled only if Person is in Types...)
template <typename T = Person>
constexpr auto getPerson()
-> std::enable_if_t<std::disjunction<std::is_same<Types, T>...>::value, T>
{
static_assert(std::is_same_v<T, Person>, "T must be of type Person!");
return std::get<T>(items_);
}
(对于未来的读者)然而,从 c++20 开始,有了
requires
约束,解决方案将会简单得多
// Method to get a Car from the Pack (enabled only if Car is in Types...)
constexpr auto getCar() requires (std::is_same_v<Types, Car> || ...)
{
return std::get<Car>(items_);
}
// Method to get a Person from the Pack (enabled only if Person is in Types...)
constexpr auto getPerson() requires (std::is_same_v<Types, Person> || ...)
{
return std::get<Person>(items_);
}