我试图将其归结为必需品。
我有一个可变参数模板类Foo,包含一个&#34;列表&#34;用其类型索引的对象。我使用函数bar<U>()
来提取该类型的元素。我使用可变参数模板和std :: enable_if来解决这个问题,只定义bar<U>()
,其中T == U.
然后我揭露所有&#34; bar&#34;来自基类的函数使用&#34;使用&#34;。
#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'
GCC至少在4.7-6.3之间编译此罚款。我首先想到这可能是因为Visual Studio 2015中缺少c ++ 11的某些功能,但令人惊讶的是,这在旧的Visual Studio 2013(MSVC 18.0)中编译得很好。 Clang也失败了。
所以我的问题是,这是这些编译器的缺点还是我在这里做的事情是不允许的?
如果我打电话给#34;吧&#34;使用类似list.bar<int>()
的硬编码类型,它会在所有经过测试的编译器上编译。
答案 0 :(得分:1)
要在此处使用enable_if
,您需要在is_same<U, T>::value == false
时提供备用选项。理想情况下,这可以完成by exposing all base class members bar
with a using-declaration ...
using Foo<Ts...>::template bar;
不幸的是,标准和it was decided not to rectify this禁止这样做。因此,我们必须以另一种方式揭露它们。因此,最简单的解决方案是为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;
};
在这些选项中,我建议使用后者,因为它会更紧密地匹配您当前的代码。
答案 1 :(得分:0)
上面不应该编译。您的enable_if
声明表示bar<U>
仅在U
为int
时才存在(即与T
相同)。因此,当您输入list.bar<T>()
时,它正在寻找一个不存在的功能。
以下内容适用,因为如果您使用int
,该功能将实际存在,因为这是您声明的内容(Foo<int, float, double> list
)。
int main()
{
bar2<int /*float*/>();
return 0;
}
编辑:只是详细说明......
我不确定你想要完成什么,但也许你希望你的班级是这样的?
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()
{
// Your "int" one will come through here.
return mObj;
}
template <typename U>
typename std::enable_if<!std::is_same<U, T>::value, void>::type
bar()
{
// Your "float" one will come through here.
}
private:
T mObj;
};