【问题标题】:What to use as replacement for concepts (upcoming feature) in C++?用什么替代 C++ 中的概念(即将推出的功能)?
【发布时间】:2014-02-03 21:34:54
【问题描述】:

用什么来替代 C++ 中的概念(即将推出的功能)?

您可能听说过 C++ 中的概念。这是一个将 允许您在模板中指定类型的要求。

我现在正在寻找一种方法来做到这一点,我发现最好的方法是 Stroustrup 的书中他将谓词与 static_assert 一起使用,如下所示:

template<typename Iter, typename Val>
Iter find(Iter b, Iter e, Val x) 
{
    static_assert(Input_iterator<Iter>(),"find(): Iter is not a Forward iterator");

    // Rest of code...
}

如果您使用其他方法或此方法有问题,请告诉我。

【问题讨论】:

  • 另一种非常常见的方法是在返回类型上或通过附加的默认模板参数使用 SFINAE。
  • @dyp 我猜提供一个简单的代码示例不会有什么坏处。
  • 目前正在寻找重复/链接,现在您至少有一个关键字;)
  • “它不会伤害到”...有趣的寻求帮助的方式。
  • Here's an answer 演示了将 SFINAE 技术与函数模板一起使用的三种方法。

标签: c++ c++11 c++-concepts


【解决方案1】:

嗯,有几次我需要类似概念的功能,我求助于Boost Concept Check。它不是最漂亮的库,但似乎已经内置了很多东西。

使用您的方法我遇到的唯一问题是需要编写所有特征类。我没有广泛使用它,但大概很多常见的东西已经用 Boost 为你完成了。

【讨论】:

    【解决方案2】:

    有一种 C++03 方法可以执行概念提供的部分编译时检查。

    检查是否提供了特定成员

    概念可以定义如下(if(0) 用于抑制链接期间的错误。(void)test# 用于抑制未使用的变量警告。):

    template <class T>
    struct ForwardIterator {
      ForwardIterator() {
        if(0) {
          void (T::* test1) () = &T::operator++; (void)test1;
        }
      }
    };
    
    template <class T>
    struct BidirectionalIterator {
      BidirectionalIterator() {
        if(0) {
          ForwardIterator<T> requirement_1;
          void (T::* test1) () = &T::operator--; (void)test1;
        }
      }
    };
    

    并且可以在编译时使用模板实例化进行测试:

    struct FooIterator {
      void operator++() {}
    };
    
    template struct BidirectionalIterator<FooIterator>;
    

    它还有一个额外的好处是提供编译错误(一旦你习惯了它们)比 C++11 的 static_assert 提供的那些更易读。例如,gcc 给出以下错误:

    concept_test.cpp: In instantiation of ‘BidirectionalIterator<T>::BidirectionalIterator() [with T = FooIterator]’:                                                                                                          
    concept_test.cpp:24:17:   required from here                                                                                                                                                                               
    concept_test.cpp:15:30: error: ‘operator--’ is not a member of ‘FooIterator’                                                                                                                                               
           void (T::* test1) () = &T::operator--; (void)test1;                                                                                                                                                                 
                                  ^                                                                                                                                                                                            
    

    甚至 MSVC2010 也会生成一个有用的编译错误,其中包括导致错误的模板参数 T 的值。它不会为 static_asserts 做到这一点。

    检查参数类型和返回值

    如果参数/返回类型依赖于被测试的类,被测试的类必须提供必要的 typedef。例如,以下概念测试一个类是否提供返回前向迭代器的开始和结束函数:

    template <class T>
    struct ForwardIterable {
      ForwardIterable() {
        if(0) {
          ForwardIterator<typename T::Iterator> requirement_1;
          typename T::Iterator (T::* test1) () = &T::begin; (void)test1;
          typename T::Iterator (T::* test2) () = &T::end;   (void)test2;
        }
      }
    };
    

    并且使用如下(注意typedef是必须的):

    struct SomeCollection {
      typedef FooIterator Iterator;
      Iterator begin();
      Iterator end();
    };
    
    template struct ForwardIterable<SomeCollection>;
    

    签名检查

    此方法还广泛检查签名。在下面的代码中,编译器会检测到modifyFooItem 的参数不应该是const。

    struct SomeFoo;
    
    template <class T>
    struct TestBar {
      TestBar() {
        if(0) {
          int (T::* test1) (SomeFoo * item) = &T::modifyFooItem; (void)test1;
        }
      }
    };
    
    struct SomeBar {
      int modifyFooItem(const SomeFoo * item) {}
    };
    
    template struct TestBar<SomeBar>;
    

    它会产生以下错误:

    concept_test.cpp: In instantiation of ‘TestBar<T>::TestBar() [with T = SomeBar]’:
    concept_test.cpp:61:17:   required from here
    concept_test.cpp:52:47: error: cannot convert ‘int (SomeBar::*)(const SomeFoo*)’ to ‘int (SomeBar::*)(SomeFoo*)’ in initialization
           int (T::* test1) (SomeFoo * item) = &T::modifyFooItem; (void)test1;
    

    【讨论】:

      【解决方案3】:

      检查概念的最佳方法是使用替换失败。但是,在 C++98 中,使用替换失败的检测是相当有限的。在 C++11 中,我们可以使用更强大的表达式替换失败。 C++11 中的Tick 库提供了定义概念谓词的简单方法。例如,一个又快又脏的is_input_iterator 可以这样写:

      TICK_TRAIT(is_input_iterator,
          std::is_copy_constructible<_>)
      {
          template<class I>
          auto requires_(I&& i) -> TICK_VALID(
              *i,
              ++i,
              i++,
              *i++
          );
      };
      

      然后Tick 还提供了一个TICK_REQUIRES 宏来添加模板约束(它只处理所有enable_if 样板),所以你可以像这样定义函数:

      template<typename Iter, typename Val, TICK_REQUIRES(is_input_iterator<Iter>())>
      Iter find(Iter b, Iter e, Val x) 
      {
          // Rest of code...
      }
      

      理想情况下,您不想使用static_assert,因为它会产生编译错误。所以我们无法检测 find 在使用某些参数调用时是否有效,因为它在无效时会产生编译器错误。

      【讨论】:

        猜你喜欢
        • 2016-08-25
        • 2015-05-23
        • 2015-06-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-11
        相关资源
        最近更新 更多