【问题标题】:Compile-time C++ function to check whether all template argument types are unique编译时 C++ 函数检查是否所有模板参数类型都是唯一的
【发布时间】:2021-11-13 02:27:15
【问题描述】:

有一个很好的问题 (Which substitution failures are not allowed in requires clauses?) 提出了下一个问题。

需要编写一个编译时函数template<typename... Ts> constexpr bool allTypesUnique(),如果所有参数类型都是唯一的,它将返回true,否则返回false。并且限制是不成对比较参数类型。不幸的是,答案只解释了为什么不能用某种特定的方法来实现这样的功能。

我认为解决方案可以使用多重继承来实现。这个想法是让一个类继承自多个类:Ts 中的每个类型 T 一个。每个这样的类都定义了一个虚拟函数,其签名取决于T。如果在Ts 中多次发现某些T,则子类中的函数f 将覆盖基类中的函数并且可以检测到:

template<typename> struct A{};

template<typename T, typename... Ts>
struct B : B<Ts...> {
    using B<Ts...>::f;
    constexpr virtual void f(A<T>, bool & unique) { if( ++count > 1 ) unique = false; }
    int count = 0;
};

template<typename T>
struct B<T> {
    constexpr virtual void f(A<T>, bool & unique) { if( ++count > 1 ) unique = false; }
    int count = 0;
};

template<typename... Ts>
constexpr bool allTypesUnique() {
    B<Ts...> b;
    bool res = true;
    ( b.f( A<Ts>{}, res ), ... );
    return res;
}

int main() {
    static_assert( allTypesUnique<void>() );
    static_assert( allTypesUnique<void, int&>() );
    static_assert( !allTypesUnique<int&, int&>() );
    static_assert( allTypesUnique<char, short, int>() );
    static_assert( !allTypesUnique<char, short, char>() );
}

演示:https://gcc.godbolt.org/z/8jhnE7P11

只是好奇,解决方案是否正确,是否有更简单的解决方案来解决这个问题?

【问题讨论】:

  • 我添加了 C++20 标签,因为在此之前,virtual 函数不能是 constexpr
  • 在给定引用类型时会中断,但应该很容易修复。
  • 为什么不能成对比较参数类型的限制?这似乎很随意。这纯粹是学术活动吗?
  • 为什么避免成对比较很重要/有用?请注意,您没有写“避免所有对比较”,您需要完全避免此类比较。这会阻止使用基于 std::is_same_v 的方法。
  • 感谢您的 cmets。我做了一个小的代码更改以支持引用类型。

标签: c++ overloading variadic-templates multiple-inheritance c++20


【解决方案1】:

在支持类大小优化的编译器中,可以通过 C++20 属性 [[no_unique_address]] 为空成员获得更简单的解决方案。如果所有类的空成员都有不同的类型,那么它的sizeof将为1。如果某些成员类型重复,则它们不能共享相同的地址,sizeof将大于1。

解决方案代码:

template<typename> struct A{};

template<typename T, typename... Ts>
struct B : B<Ts...> {
    [[no_unique_address]] A<T> a;
};

template<typename T>
struct B<T> {
    [[no_unique_address]] A<T> a;
};

template<typename... Ts>
constexpr bool allTypesUnique() {
    if constexpr (sizeof...(Ts) <= 1 )
        return true;
    else
        return sizeof(B<Ts...>) == 1;
}

int main() {
    static_assert( allTypesUnique<void>() );
    static_assert( allTypesUnique<void, int&>() );
    static_assert( !allTypesUnique<int&, int&>() );
    static_assert( allTypesUnique<char, short, int>() );
    static_assert( !allTypesUnique<char, short, char>() );
}

演示:https://gcc.godbolt.org/z/577EP1774

【讨论】:

    【解决方案2】:

    可以构造一个constexpr 函数来回答这个问题:

    template <class T, class... Ts>
    constexpr bool unique_types()
    {
        if constexpr (sizeof...(Ts)) {
            return !(std::is_same_v<T,Ts> || ...) && unique_types<Ts...>();
        }
        return true;
    }
    

    Demo

    上面的代码相当于编译时间

    for (int i(0); i < n; ++i)
      for (int j(i + 1); j < n; ++j)
        { /* check type[i] vs type[j] */ }
    

    如果您想要比较的任何变化(例如,考虑 intint&amp; 相同),只需修改 std::is_same_v(例如 std::is_same_v&lt;std::decay_t&lt;T&gt;, std::decay_t&lt;Ts&gt;&gt;

    【讨论】:

    • OP 已明确声明 the restriction is not to compare the argument types pairwise.
    • @SergeyKolesnik 我最关心的是is there a simpler solution for this problem? 部分
    • the restriction is not to compare the argument types pairwise 将您的解决方案从一组可能的解决方案中排除,以便在is there a simpler solution for this problem? 之间进行比较
    【解决方案3】:

    如果您根据每个给定的类型使用虚拟基类,您将为生成的类中的每个唯一类型获得一个准确的基类实例。如果给定类型的数量是生成的基类的数量,则每种类型都是唯一的。您可以通过其大小“测量”生成的基类的数量,但必须注意您有一个 vtable 指针,其大小取决于实现。因此,每个生成的类型都应该足够大以隐藏对齐问题。

    顺便说一句:它也适用于引用类型。

    
    template < typename T> struct AnyT { char i[128]; };
    
    template < typename FIRST, typename ... T>
    struct CheckT: virtual AnyT<FIRST>, virtual CheckT<T...> { };
    
    template < typename FIRST >
    struct CheckT<FIRST>: virtual AnyT<FIRST> {};
    
    
    template < typename ... T>
    constexpr bool allTypesUnique()
    {
        using T1 = CheckT<int>;
        using T2 = CheckT<bool, int>;
    
        constexpr std::size_t s1 = sizeof( T1 );
        constexpr std::size_t s2 = sizeof( T2 );
        constexpr std::size_t diff = s2 - s1; 
        constexpr std::size_t base = s1 - diff;
        constexpr std::size_t measure = sizeof( CheckT< T...> );
    
        return !((sizeof...(T)*diff+base) - measure);
    }
    
    
    int main() {
        static_assert( allTypesUnique<void>() );
        static_assert( allTypesUnique<void, int>() );
        static_assert( !allTypesUnique<void, void>() );
        static_assert( allTypesUnique<char, short, int>() );
        static_assert( !allTypesUnique<char, short, char>() );
    }
    

    Demo

    【讨论】:

      【解决方案4】:

      我认为您可以尝试详细说明使用编译时类型 ID。 这是一个部分实现的 C++14 版本(需要编译时排序):

      #include <cstddef>
      #include <type_traits>
      #include <utility>
      
      namespace detail {
          template<typename T>
          struct type_id {
              constexpr static int value{};
          };
      
          template<const int *const A, const int *const B>
          struct same_address : std::false_type {};
      
          template<const int *const A>
          struct same_address<A, A> : std::true_type {};
      
      }// namespace detail
      
      // TODO: implement
      template<const int *const... I>
      struct sort_array {
          using type = std::integer_sequence<const int *const, I...>;
      };
      
      template<typename>
      struct find_duplicates;
      
      template<const int *const A, const int *const B, const int *const... I>
      struct find_duplicates<std::integer_sequence<const int *const, A, B, I...>> {
          constexpr static bool value = std::conditional_t<detail::same_address<A, B>::value,
                                                           std::true_type,
                                                           find_duplicates<std::integer_sequence<const int *const, B, I...>>>::value;
      };
      
      template<>
      struct find_duplicates<std::integer_sequence<const int *const>> {
          constexpr static bool value = false;
      };
      
      template<const int *const I>
      struct find_duplicates<std::integer_sequence<const int *const, I>> {
          constexpr static bool value = false;
      };
      
      template<typename... T>
      constexpr bool all_types_unique() {
          return !find_duplicates<typename sort_array<&detail::type_id<T>::value...>::type>::value;
      };
      
      int main() {
          static_assert(detail::same_address<&detail::type_id<int>::value,
                                             &detail::type_id<int>::value>::value,
                        "");
          static_assert(!detail::same_address<&detail::type_id<int>::value,
                                              &detail::type_id<double>::value>::value,
                        "");
      
          static_assert(all_types_unique<>(), "");
          static_assert(all_types_unique<int>(), "");
          static_assert(all_types_unique<int, double>(), "");
          static_assert(all_types_unique<int, double, char>(), "");
          static_assert(!all_types_unique<int, int>(), "");
          static_assert(!all_types_unique<int, int, int>(), "");
      
          return 0;
      }
      

      https://godbolt.org/z/E4G6YchE5

      【讨论】:

      • @Fedor 我从答案中删除了它。
      猜你喜欢
      • 2016-10-03
      • 2021-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-11
      • 1970-01-01
      • 2014-03-31
      • 1970-01-01
      相关资源
      最近更新 更多