我有一个通用的矢量类,我在其中实现了一个接受仿函数的构造函数。这个想法是调用仿函数来初始化向量中的每个元素,仿函数的单个参数是元素索引。
然而,当传递与矢量元素类型不同类型的标量时,这会干扰我的“来自标量”构造函数:
template <typename T, int N>
struct SomeVectorClass
{
T values[N];
SomeVectorClass() {}
SomeVectorClass(T scalar)
{
for (int i = 0; i < N; i++) values[i] = scalar;
}
template <typename InitFunctionType>
SomeVectorClass(InitFunctionType initFunction)
{
for (int i = 0; i < N; i++) values[i] = initFunction(i);
}
};
...
SomeVectorClass<int, 2> a([&] (int i) { return i; }); // This works
SomeVectorClass<int, 2> b(123); // This works
SomeVectorClass<int, 2> c(123.f); // This causes error "called object type 'float' is not a function or function pointer"
我对c
的期望是编译器自动将123.f
转换为123
,然后调用“来自标量”构造函数(接受int
in这种情况)。
三个问题:
std::enable_if
或类似的黑客?std::enable_if
是仿函数,我将如何使用InitFunctionType
仅启用仿函数构造函数?提前感谢您的时间!
编辑:由于性能原因,忘记提及我不能使用std::function
。该课程需要内联友好。
答案 0 :(得分:2)
构造函数模板是首选,因为它是完全匹配,而T
构造函数需要转换。完全匹配是首选。
这正是enable_if
存在要解决的问题。您需要从重载决策集中删除构造函数模板,除非它实际上可以使用int
调用:
template <typename F,
typename = typename std::result_of <F(int)>::type>
SomeVectorClass (F func) { }
这样,使用float
或char
构建将选择您的T
构造函数。这是唯一的候选人!
注意:std::result_of
不一定是SFINAE友好的(N3462)。以上内容适用于gcc 4.9.2,但不适用于gcc 4.7.2。如果上述内容无法编译,请使用类似
include / c ++ / 4.7.2 / type_traits:1834:9:错误:'
std::declval<float>()
'不能用作函数
然后你可以将“functor”construtor重写为:
template <typename F,
typename = decltype(std::declval<F>()(std::declval<int>()))
SomeVectorClass (F func) { }
答案 1 :(得分:0)
这是一个C ++ 11代码墙,其中包含编译器可能没有的一堆C ++ 14特性,以及一些有用的元编程机制:
template<class T>struct tag{using type=T;}; // hold tags
template<class Tag>using type_t=typename Tag::type; // get types from tags
template<class...>struct voider:tag<void>{}; // everything goes to void
template<class...Ts>using void_t=type_t<voider<Ts...>>; // nothing is
template<class...>struct types{using type=types;}; // bundle of types
namespace details {
// SFINAE friendly result_of:
template<class Sig, class=void>
struct invoke;
template<class F, class...Args>
struct invoke<F(Args...),
void_t< decltype( std::declval<F>()(std::declval<Args>()...)) >
>:tag< decltype( std::declval<F>()(std::declval<Args>()...)) > {};
};
// C++14 style type alias for the above:
template<class Sig>
using invoke_t = type_t<details::invoke<Sig>>;
namespace details {
// can apply takes a template and some types
// and is true iff applying the types to the template is valid:
template<template<class...>class Z, class types, class=void>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, types<Ts...>, void_t<Z<Ts...>>>:
std::true_type
{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,types<Ts...>>;
// together, this gives us a "can we invoke a signature"
// in one line:
template<class Sig>
using can_invoke=can_apply< invoke_t, Sig >;
// C++14 feature, want it in C++11:
template<bool b, class X=void>
using enable_if_t=type_t<std::enable_if<b,X>>
现在,上述问题很简单:
template<class F, class=enable_if_t< can_invoke<F&(int)>::value >>
SomeVectorClass(F&& initFunction)
{
for (int i = 0; i < N; i++)
values[i] = initFunction(i);
}
易于阅读。始终尝试在实用程序标头中隐藏讨厌的decltype
内容,不要将其与最终用户代码混合使用。