如果类型包包含特定类型,如何仅向类添加方法?

问题描述 投票:0回答:1

我正在尝试创建一个可以包含类型包的类。

// 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()
通话中。

我可能误解了这一切是如何运作的。我找到了一些有些相关的答案,但没有一个答案能够根据我的需要正确组合因素。

这个功能有可能实现吗?如果是的话,怎么办?

c++ templates c++17 variadic-templates class-template
1个回答
3
投票

这个功能有可能实现吗?如果是的话,怎么办?

是的,这是可能的。最小的改变解决方案是没有

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_);
}

观看现场演示


(对于未来的读者)然而,从 开始,有了

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_);
}

观看现场演示

© www.soinside.com 2019 - 2024. All rights reserved.