如何检查特定模板参数是否相同

How do I check that a particular template parameter is the same

我有一个带有三个模板参数的 class:

template<typename A, typename B, typename C>
class Unit;

然后我有一个代表这个 class 及其所有专业化的概念:

template <typename T>
struct is_unit : std::false_type {};

template<typename A, typename B, typename C>
struct is_unit<Unit<A, B, C>> : std::true_type {};

template <typename T>
constexpr bool is_unit_v = is_unit<T>::value;

template <typename T>
concept Unit_T = is_unit_v<T>;

在 Unit class 的定义中,我想要一个函数 return 是一个与调用该函数的函数具有不同专业化的 Unit。我希望用户提供所需的 return 类型。到目前为止我有这个工作:

template<typename A, typename B, typename C>
class Unit {
public:
    // Other member stuff...

    template<Unit_T U>
    U as() { return U(*this); }
}

Unit<MyType, long double, MyOtherType> unit;
// Do stuff to unit.
auto unit2 = unit.as<Unit<MyType, long int, AnotherType>();

一切如期进行。但是,还有一个要求,我不知道如何实施。所需类型的第一个模板参数必须与调用它的类型的第一个模板参数相匹配。所以这个:

Unit<MyType, long double, MyOtherType> unit;
// Do stuff to unit.
auto unit2 = unit.as<Unit<YetAnotherType, long int, AnotherType>();

不应该编译。

我认为正确的做法应该是这样的:

template<typename A, typename B, typename C>
class Unit {
public:
    // Other member stuff...

    template<Unit_T U>
    requires std::is_same_v<U<D>, D>
    // or maybe std::is_same_V<U::D, D> ?
    U as() { return U(*this); }
}

但这不起作用。而且,据我所知,即使这是要求模板参数为正确类型的正确方法,我也无法进一步限制概念。

我尝试为具有特定第一个模板参数的单元编写另一个概念:

template<typename U, typename D>
concept UnitFirstType = is_unit_v<U> && std::is_same_v<U<D> /* or U::D */, D>;

但这不起作用。

问题似乎在于我如何使用 std::is_same_v。我只是不知道如何将它与模板参数一起使用。

那么实现这一目标的正确方法是什么:

Unit<MyType, long double, MyOtherType> unit;
auto unit2 = unit.as<Unit<MyType, long int, AnotherType>(); // will compile
// auto unit3 = unit.as<Unit<YetAnotherType, long int, AnotherType>(); // should not compile

这可能就是您想要的

template<typename A, typename B, typename C>
class Unit;

template<typename U, typename A>
constexpr bool unit_first_type = false;

template<typename A, typename B, typename C>
constexpr bool unit_first_type<Unit<A, B, C>, A> = true;

template<typename U, typename A>
concept UnitFirstType = unit_first_type<U, A>;

template<typename A, typename B, typename C>
class Unit {
 public:
  // Other member stuff...
  template<UnitFirstType<A> U>
  U as();
};

Demo

我想你可以使用 good-old(C++20 之前)SFINAE。

例如,给定一个自定义类型特征如下

template <typename, typename>
struct firstEqual : public std::false_type
{};

template <typename A, typename B1, typename C1, typename B2, typename C2>
struct firstEqual<Unit<A, B1, C1> const, Unit<A, B2, C2> const>
   : public std::true_type
{};

您可以enable/disableas()如下

template <typename U>
std::enable_if_t<firstEqual<Unit<A, B, C> const, U const>::value, U>
   as() { return U{}; }

观察const的使用,接受不同Unit具有不同constness但相同的第一个模板参数的情况......以防万一是你想要的。

另请注意,您不再需要 Unit_T 概念。

下面是一个完整的编译示例

#include <type_traits>

template<typename A, typename B, typename C>
class Unit;

template <typename, typename>
struct firstEqual : public std::false_type
{};

template <typename A, typename B1, typename C1, typename B2, typename C2>
struct firstEqual<Unit<A, B1, C1> const, Unit<A, B2, C2> const>
   : public std::true_type
{};

template <typename A, typename B, typename C>
class Unit {
  public:
    // Other member stuff...

    template <typename U>
    std::enable_if_t<firstEqual<Unit<A, B, C> const, U const>::value, U>
       as() { return U{}; }
};



int main()
{
  Unit<int, long double, void> unit;

  auto unit2 = unit.as<Unit<int, long, float>>();  // compile
  //auto unit3 = unit.as<Unit<char, long, float>>(); // compilation error
}