我试图将其归结为要点。我有一个可变的模板类Foo,其中包含用其类型索引的对象的“列表”。我使用函数bar<U>()
来提取该类型的元素。我使用可变参数模板和std :: enable_if来解决此问题,仅定义bar<U>()
T ==U。然后使用“ using”公开基类中的所有“ bar”函数。
#include <type_traits>
template<typename... Ts>
class Foo
{
public:
void bar() {}
};
template<typename T, typename... Ts>
class Foo<T, Ts...> : public Foo<Ts...>
{
public:
using Foo<Ts...>::bar;
template<typename U>
typename std::enable_if<std::is_same<U, T>::value, U >::type
bar()
{
return mObj;
}
private:
T mObj;
};
template<typename T>
void bar2()
{
Foo<int, float, double> list;
list.bar<T>();
}
int main()
{
bar2<float>();
return 0;
}
除了在Clang和Visual Studio 2015上之外,这都工作得很好。尝试了MSVC 19.0和19.10并给出错误:
Compiled with /EHsc /nologo /W4 /c
main.cpp
main.cpp(30): error C2672: 'Foo<int,float,double>::bar': no matching overloaded function found
main.cpp(35): note: see reference to function template instantiation 'void bar2<float>(void)' being compiled
main.cpp(30): error C2770: invalid explicit template argument(s) for 'std::enable_if<std::is_same<U,T>::value,U>::type Foo<int,float,double>::bar(void)'
with
[
T=int
]
main.cpp(18): note: see declaration of 'Foo<int,float,double>::bar'
至少在4.7-6.3之间的GCC可以编译此罚款。我首先认为这可能是Visual Studio 2015中缺少c ++ 11的某些功能,但是令人惊讶的是,这在较旧的Visual Studio 2013(MSVC 18.0)中可以很好地编译。lang也失败。
所以我的问题是,这是这些编译器的缺点,还是我在这里不允许做的事情?
如果我用硬编码类型调用“ bar”,list.bar<int>()
它将在所有经过测试的编译器上编译。
要enable_if
在此处使用,您需要为when提供一个替代选项is_same<U, T>::value == false
。理想情况下,这可以通过向所有基类成员bar
使用using声明来实现。
using Foo<Ts...>::template bar;
不幸的是,这是标准所禁止的,因此决定不对此进行纠正。因此,我们必须以其他方式公开它们。因此,最简单的解决方案是为制作包装Foo<Ts...>::template bar()
,如下所示:
template<typename T, typename... Ts>
class Foo<T, Ts...> : public Foo<Ts...>
{
public:
// using Foo<Ts...>::template bar; // Sadly, this is forbidden.
template<typename U>
typename std::enable_if<std::is_same<U, T>::value, U >::type
bar()
{
return mObj;
}
// Additional wrapper, to compensate for lack of valid syntax.
template<typename U>
typename std::enable_if<!std::is_same<U, T>::value, U >::type
bar()
{
return Foo<Ts...>::template bar<U>();
}
private:
T mObj;
};
但是请注意,Foo<Ts...>::bar()
由于返回,包装器无法调用void
。假定这是通用情况,打算在U
不属于包的情况下使用,有两种方法可以纠正此问题:
修改Foo<Ts...>::bar()
。
template<typename... Ts>
class Foo
{
public:
template<typename T>
T bar()
{
// Return an invalid value.
return T{-1};
}
};
提供的第三版Foo<T, Ts...>::bar()
,当U
不是成员时使用T, Ts...
; 这一回Foo<Ts...>::bar()
。为此,定义一个特征以检测其是否在包装中将很有用。
template<typename...>
struct is_in_pack : std::false_type {};
template<typename U, typename T1, typename... Ts>
struct is_in_pack<U, T1, Ts...> :
std::integral_constant<bool,
std::is_same<U, T1>::value ||
is_in_pack<U, Ts...>::value>
{};
然后,我们只需要使用该特征。
template<typename T, typename... Ts>
class Foo<T, Ts...> : public Foo<Ts...>
{
public:
// using Foo<Ts...>::template bar; // Sadly, this is forbidden.
template<typename U>
typename std::enable_if<std::is_same<U, T>::value, U >::type
bar()
{
return mObj;
}
// Additional wrapper, to compensate for lack of valid syntax.
// U is a member of <T, Ts...>.
template<typename U>
typename std::enable_if<!std::is_same<U, T>::value &&
is_in_pack<U, T, Ts...>::value, U >::type
bar()
{
return Foo<Ts...>::template bar<U>();
}
// Additional wrapper, to compensate for lack of valid syntax.
// U isn't a member of <T, Ts...>.
template<typename U>
typename std::enable_if<!is_in_pack<U, T, Ts...>::value>::type
bar()
{
return Foo<>::bar();
}
private:
T mObj;
};
在这些选项中,我建议使用后者,因为它与您当前的代码更加匹配。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句