是的,lite的概念基本上是在打扮SFINAE。另外,它还可以进行更深入的自省,以实现更好的重载。但是,仅当概念谓词定义为时,该方法才有效concept bool
。改进的重载不适用于当前概念谓词,但可以使用条件重载。让我们看看如何在C ++ 14中定义谓词,约束模板和重载函数。这有点长,但是它涵盖了如何创建在C ++ 14中完成此操作所需的所有工具。
定义谓词
首先,它是样的丑陋与所有的读谓词std::declval
和decltype
无处不在。相反,我们可以利用以下事实:可以使用尾随的decltype约束函数(来自Eric Niebler的博客此处),如下所示:
struct Incrementable
{
template<class T>
auto requires_(T&& x) -> decltype(++x);
};
因此,如果++x
无效,则requires_
成员函数不可调用。因此,我们可以使用以下方法创建一个models
仅检查是否requires_
可调用的特征void_t
:
template<class Concept, class Enable=void>
struct models
: std::false_type
{};
template<class Concept, class... Ts>
struct models<Concept(Ts...), void_t<
decltype(std::declval<Concept>().requires_(std::declval<Ts>()...))
>>
: std::true_type
{};
约束模板
因此,当我们想基于概念来约束模板时,我们仍然需要使用enable_if
,但是我们可以使用此宏来使其变得更整洁:
#define REQUIRES(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0
因此,我们可以定义一个increment
受Incrementable
概念约束的函数:
template<class T, REQUIRES(models<Incrementable(T)>())>
void increment(T& x)
{
++x;
}
因此,如果我们increment
使用not进行调用Incrementable
,则会得到如下错误:
test.cpp:23:5: error: no matching function for call to 'incrementable'
incrementable(f);
^~~~~~~~~~~~~
test.cpp:11:19: note: candidate template ignored: disabled by 'enable_if' [with T = foo]
template<class T, REQUIRES(models<Incrementable(T)>())>
^
重载功能
现在,如果要执行重载,则要使用条件重载。假设我们要创建一个std::advance
使用概念谓词,我们可以这样定义它(现在我们将忽略可递减的情况):
struct Incrementable
{
template<class T>
auto requires_(T&& x) -> decltype(++x);
};
struct Advanceable
{
template<class T, class I>
auto requires_(T&& x, I&& i) -> decltype(x += i);
};
template<class Iterator, REQUIRES(models<Advanceable(Iterator, int)>())>
void advance(Iterator& it, int n)
{
it += n;
}
template<class Iterator, REQUIRES(models<Incrementable(Iterator)>())>
void advance(Iterator& it, int n)
{
while (n--) ++it;
}
但是,这会导致模棱两可的重载(在精简的概念中,除非我们将谓词concept bool
与std::vector
迭代器一起使用,否则除非我们将谓词更改为引用a中的其他谓词,否则这仍将是模棱两可的重载)。我们想要做的是对调用进行排序,我们可以使用条件重载来完成。可以考虑编写这样的内容(无效的C ++):
template<class Iterator>
void advance(Iterator& it, int n) if (models<Advanceable(Iterator, int)>())
{
it += n;
}
else if (models<Incrementable(Iterator)>())
{
while (n--) ++it;
}
因此,如果没有调用第一个函数,它将调用下一个函数。因此,让我们从实现两个功能开始。我们将创建一个名为的类,该类basic_conditional
接受两个函数对象作为模板参数:
struct Callable
{
template<class F, class... Ts>
auto requires_(F&& f, Ts&&... xs) -> decltype(
f(std::forward<Ts>(xs)...)
);
};
template<class F1, class F2>
struct basic_conditional
{
template<class... Ts>
auto operator()(Ts&&... xs) -> decltype(F1()(std::forward<Ts>(xs)...))
{
return F1()(std::forward<Ts>(xs)...);
}
template<class... Ts, REQUIRES(!models<Callable(F1, Ts&&...)>())>
auto operator()(Ts&&... xs) -> decltype(F2()(std::forward<Ts>(xs)...))
{
return F2()(std::forward<Ts>(xs)...);
}
};
所以现在这意味着我们需要将函数定义为函数对象:
struct advance_advanceable
{
template<class Iterator, REQUIRES(models<Advanceable(Iterator, int)>())>
void operator()(Iterator& it, int n) const
{
it += n;
}
};
struct advance_incrementable
{
template<class Iterator, REQUIRES(models<Incrementable(Iterator)>())>
void operator()(Iterator& it, int n) const
{
while (n--) ++it;
}
};
static conditional<advance_advanceable, advance_incrementable> advance = {};
所以现在,如果我们尝试将其与一起使用std::vector
:
std::vector<int> v = { 1, 2, 3, 4, 5, 6 };
auto iterator = v.begin();
advance(iterator, 4);
std::cout << *iterator << std::endl;
它将编译并打印出来5
。
但是,std::advance
实际上有三个重载,因此我们可以使用basic_conditional
来实现conditional
适用于任何使用递归的函数的重载:
template<class F, class... Fs>
struct conditional : basic_conditional<F, conditional<Fs...>>
{};
template<class F>
struct conditional<F> : F
{};
因此,现在我们可以std::advance
像这样编写完整内容:
struct Incrementable
{
template<class T>
auto requires_(T&& x) -> decltype(++x);
};
struct Decrementable
{
template<class T>
auto requires_(T&& x) -> decltype(--x);
};
struct Advanceable
{
template<class T, class I>
auto requires_(T&& x, I&& i) -> decltype(x += i);
};
struct advance_advanceable
{
template<class Iterator, REQUIRES(models<Advanceable(Iterator, int)>())>
void operator()(Iterator& it, int n) const
{
it += n;
}
};
struct advance_decrementable
{
template<class Iterator, REQUIRES(models<Decrementable(Iterator)>())>
void operator()(Iterator& it, int n) const
{
if (n > 0) while (n--) ++it;
else
{
n *= -1;
while (n--) --it;
}
}
};
struct advance_incrementable
{
template<class Iterator, REQUIRES(models<Incrementable(Iterator)>())>
void operator()(Iterator& it, int n) const
{
while (n--) ++it;
}
};
static conditional<advance_advanceable, advance_decrementable, advance_incrementable> advance = {};
Lambdas重载
但是,此外,我们可以使用lambda代替函数对象来编写它,这可以帮助使其更清晰地编写。因此,我们STATIC_LAMBDA
在编译时使用此宏来构造lambda:
struct wrapper_factor
{
template<class F>
constexpr wrapper<F> operator += (F*)
{
return {};
}
};
struct addr_add
{
template<class T>
friend typename std::remove_reference<T>::type *operator+(addr_add, T &&t)
{
return &t;
}
};
#define STATIC_LAMBDA wrapper_factor() += true ? nullptr : addr_add() + []
并添加一个make_conditional
功能是constexpr
:
template<class... Fs>
constexpr conditional<Fs...> make_conditional(Fs...)
{
return {};
}
然后,我们现在可以这样编写advance
函数:
constexpr const advance = make_conditional(
STATIC_LAMBDA(auto& it, int n, REQUIRES(models<Advanceable(decltype(it), int)>()))
{
it += n;
},
STATIC_LAMBDA(auto& it, int n, REQUIRES(models<Decrementable(decltype(it))>()))
{
if (n > 0) while (n--) ++it;
else
{
n *= -1;
while (n--) --it;
}
},
STATIC_LAMBDA(auto& it, int n, REQUIRES(models<Incrementable(decltype(it))>()))
{
while (n--) ++it;
}
);
与使用功能对象版本相比,这更加紧凑和可读。
另外,我们可以定义一个modeled
减少decltype
丑陋的函数:
template<class Concept, class... Ts>
constexpr auto modeled(Ts&&...)
{
return models<Concept(Ts...)>();
}
constexpr const advance = make_conditional(
STATIC_LAMBDA(auto& it, int n, REQUIRES(modeled<Advanceable>(it, n)))
{
it += n;
},
STATIC_LAMBDA(auto& it, int n, REQUIRES(modeled<Decrementable>(it)))
{
if (n > 0) while (n--) ++it;
else
{
n *= -1;
while (n--) --it;
}
},
STATIC_LAMBDA(auto& it, int n, REQUIRES(modeled<Incrementable>(it)))
{
while (n--) ++it;
}
);
最后,如果您对使用现有的库解决方案感兴趣(而不是像我展示的那样滚动自己的库解决方案)。还有就是蜱库,提供定义概念和约束模板的框架。而飞度库可以处理的功能和超载。