Concept is a term that describes a named set of requirements for a type.

Motivation of Concept

C++的Concept主要是为了对模板参数进行约束,来解决模板参数出错是出错信息太长的问题。引入Concept的约束可以很好地 改善这一状况。另一方面,一个C++模板类中,没有用到的函数是不予编译的,也就无法检测出可能存在的bug,使用Concept 可以对模板的类型参数设置一些必要的约束。

然而Concept最终还是不能进入C++17。

requires

首先实现requires,是的当类型参数不满足要求时能够阻止模板继续编译。一个可行的思路是使用std::enable_if

template <bool Pred>
using Requires = typename std::enable_if<Pred>::type;

用法:

template <typename A, typename B, typename = Requires<std::is_convertible<A, B>::value>>
bool is_equal(A const & a, B const & b) {
    return a == b;
}

bool fn() {
    std::string s;
    return is_equal(s, 1);
}

使用Requires,GCC 的报错信息为(gcc 6.0 expreimental, g++ -c -std=c++11):

a.cxx: In function 'bool fn()':
a.cxx:33:25: error: no matching function for call to 'is_equal(std::__cxx11::string&, int)'
     return is_equal(s, 1);
                         ^
a.cxx:27:6: note: candidate: template<class A, class B, class> bool is_equal(const A&, const B&)
     bool is_equal(A const & a, B const & b) {
          ^~~~~~~~
a.cxx:27:6: note:   template argument deduction/substitution failed:

而如果不使用Requires,GCC 产生了超过300行的错误信息,非常不利于定位和修复错误。

Concept

而Concept的实现可以大致分为这样几个类型。

std::is_xx

<type_traits>头文件中提供了一系列类似于std::is_xxx的函数,可以使用这些函数来判断一个类型是否满足某些性质, 使之成为一个Concept。举例:

/**
 * Specifies that an object of the type can be constructed from rvalue.
 */
template <typename T>
struct MoveConstructible {
    static constexpr bool value = std::is_move_constructible<T>::value;
    constexpr operator bool() const noexcept { return value; }
};

SFINAE

SFINAE 即 “Substitution Failure Is Not An Error”,指的是当模板类型参数替换发生错误时,该模板特化定义会被忽略 而不是被认为错误。这一特性可以用来判断某个类型是否具有制定的函数。例如EuqalityComparable这一Concept的实现:

/**
 * Operator== is an equivalence relation.
 */
namespace _inner_impl {
// Check `==` using SFINAE.
template <typename A, typename B>
auto test_eq(decltype(std::declval<A>() == std::declval<B>()) *)
        -> decltype(std::declval<A>() == std::declval<B>());
template <typename A, typename B>
auto test_eq(...) -> void;
};

template <typename A, typename B>
struct EuqalityComparable {
    static constexpr bool value =
            std::is_convertible<decltype(_inner_impl::test_eq<A, B>(nullptr)),
                                bool>::value;
    constexpr operator bool() const noexcept { return value; }
};

首先使用SFINAE,根据模板替换的优先级,第一个test_eq的参数更加特化,会被优先考虑。如果A类型的值和B类型的 值之间不能使用==来作比较,模板参数替换就会失败,而第二个test_eq的参数类型为(...),会无条件匹配成功。如果 AB两个类型的值之间能够进行==比较,test_eq的返回值类型将会是bool,如果不能,返回值类型将会是void。 根据这一点区别,就可以得到EqualityComparable这个Concept。以此类推,要想判断一个类是否具有某个指定的成员函数、 成员变量,以及某个函数是否可以作用在指定类型的值上,都可以通过SFINAE来实现。

Iterator

对于Iterator的约束,可以使用iteator_tag来实现。例如RandomAccessIterator

template <typename Iterator>
struct RandomAccessIterator {
    static constexpr bool value = std::is_same<
            std::random_access_iterator_tag,
            typename std::iterator_traits<Iterator>::iterator_category>::value;
    constexpr operator bool() const noexcept { return value; }
};

Template Instance

对于模板类型和模板实例类型,可以判断二者之间是否存在关系,例如std::vectorstd::vector<int>的模板。

/**
 * Check if a type is the base template of another parametrised type.
 *
 * e.g.:
 *      BaseTemplate<std::vector<int>, std::vector> = true
 *      BaseTemplate<std::vector<int>, std::map> = false
 */
template <typename T, template <typename...> class U>
struct BaseTemplate {
    static constexpr bool value = false;
};

template <template <typename...> class U, typename... Ts>
struct BaseTemplate<U<Ts...>, U> {
    static constexpr bool value = true;
};

此外还可以判断两个类型实例类型对应的模板是否相同,例如std::vector<int>std::vector<string>对应着相同的模板 std::vector,而它们与std::list<int>的模板不同。

template <typename T, typename U>
struct SameTemplate;

template <template <typename...> class T, template <typename...> class U,
          typename... Ts,
          typename... Us  // Here, `Us` is just a placeholder, indicates that
                          // `U` is a parameterised
                          // tempalte.
          >
struct SameTemplate<T<Ts...>, U<Us...>> {
    static constexpr bool value = std::is_same<T<Ts...>, U<Ts...>>::value;
};

完整实现

type_concepts.hpp 中包含一个对Library Concepts中定义的Concepts的一个完整实现。

参考

  1. C++ 中的Concepts列表:Library Concepts