以下代码使用g ++ 4.8.1(mingw)以及http://gcc.godbolt.org/上的各种最新clang和gcc版本正确编译,但是对于MSVC2013 Update 4,它失败了,显然是由于typedef typename A<T>::value_type value_type;
线。编译器给出以下错误:
x.cpp(30):错误C2893:无法专门化功能模板“
void B<C,int>::bar(void)
” 使用以下模板参数: 'MemberFn=void C::baz(int)
'
更简单的typedef typedef T value_type;
可以使用。
我做错了吗?或者这是Microsoft C ++编译器中的已知错误?
补充问题:
typedef T value_type;
)或从基类中引入它们会更加惯用(例如{{ 1}},或者在C ++ 11中typedef typename A<T>::value_type value_type;
)? (注意:我现在正在对C ++ 03的兼容性进行套期保值,因此避免使用using typename A<T>::value_type;
。)这里有一些不确定的讨论:Use typedef/using from templated base class in derived class我问的原因是{{1}无论如何,我是首选,我不需要太担心。using
更新#1:这是来自框架的简化测试用例。我不是要求对结构进行一般批评。如果没有上下文,我不希望这个结构有意义。
更新#2:以下是一个相关问题,讨论typedef T value_type;
与#include <cstdio>
template <typename T>
struct A {
typedef A<T> base_type;
typedef T value_type;
};
template <typename Derived, typename T>
struct B : public A<T> {
typedef Derived derived_type;
//typedef T value_type; // this works
typedef typename A<T>::value_type value_type; // this fails in MSVC 2013
//using typename A<T>::value_type; // this fails in MSVC 2013 too
template<void (derived_type::*MemberFn)(value_type) >
void bar()
{
(static_cast<derived_type*>(this)->*MemberFn)(42);
}
};
struct C : public B<C, int> {
void baz(int i)
{
std::printf("baz(%d)\n", i);
}
void foo()
{
bar<&C::baz>();
}
};
int main(int, char *[])
{
C c;
c.foo();
}
结合使用是否有效:C++ template inheritance issue with base types
更新#3:我已在Microsoft Connect上提交了公开错误报告。如果您可以重现该问题,并认为它是一个错误,请提出错误信息:https://connect.microsoft.com/VisualStudio/feedback/details/1740423
答案 0 :(得分:1)
看起来正确。我用VS2015测试了它,同样的错误信息。实际上,它应该在没有B中的typedef的情况下工作,因为它从A继承了'value_type',所以如果是struct B,它也在命名空间中。
答案 1 :(得分:1)
我有一个可能的解决方案(我只能用VS2015进行测试)
如果您使用基本类型作为模板参数本身,它将被解析,您可以访问value_type
。
template <typename T>
struct A {
typedef T value_type;
typedef A<T> base_type;
};
template <typename Derived, typename T, typename Base = A<T>>
struct B : public Base
{
typedef Derived derived_type;
typedef void (derived_type::*member_func_type)(typename Base::value_type);
template<member_func_type MemberFn>
void bar()
{
(static_cast<derived_type*>(this)->*MemberFn)(42);
}
};
struct C : public B<C, int>
{
void baz(int i)
{
std::printf("baz(%d)\n", i);
}
void foo()
{
bar<&C::baz>();
}
};
或者我更喜欢的解决方案:
template <typename T>
struct A {
typedef A<T> base_type;
typedef T value_type;
};
template <typename Derived, typename T, typename Base = A<T>>
struct B : public Base
{
typedef Derived derived_type;
typedef typename Base::base_type base_type;
typedef typename Base::value_type value_type;
typedef void (derived_type::*member_func_type)(value_type);
template<member_func_type MemberFn>
void bar()
{
(static_cast<derived_type*>(this)->*MemberFn)(42);
}
};
我必须承认这个解决方案有它的缺点,比如用其他东西覆盖第3个模板参数的能力。至少其他基类需要声明base_type
和value_type
。
修改强>
使用static_assert
可能会阻止更改Base
模板参数。
template <typename Derived, typename T, typename Base = A<T> >
struct B : public Base
{
static_assert(std::is_same<Base, typename A<T>>::value, "Redefinition of template parameter Base is not allowed");
typedef Derived derived_type;
typedef typename Base::base_type base_type;
typedef typename Base::value_type value_type;
typedef void (derived_type::*member_func_type)(value_type);
template<member_func_type MemberFn>
void bar()
{
(static_cast<derived_type*>(this)->*MemberFn)(42);
}
};
示例:
template <typename T>
struct D {
typedef D<T> base_type;
typedef T value_type;
};
struct E : public B<C, int, D<int>>
{
};
结果:
error C2338: Redefinition of template parameter Base is not allowed
<强>更新强>
更改B
的模板参数的顺序会更改行为。这里原始代码只有T
和Derived
的顺序发生了变化。
#include <cstdio>
template <typename T>
struct A {
typedef A<T> base_type;
typedef T value_type;
};
template <typename T, typename Derived>
struct B : public A<T> {
typedef Derived derived_type;
//typedef T value_type; // this works
typedef typename A<T>::value_type value_type; // this fails in MSVC 2013
//using typename A<T>::value_type; // this fails in MSVC 2013 too
template<void (derived_type::*MemberFn)(value_type) >
void bar()
{
(static_cast<derived_type*>(this)->*MemberFn)(42);
}
};
struct C : public B<int, C> {
void baz(int i)
{
std::printf("baz(%d)\n", i);
}
void foo()
{
bar<&C::baz>();
}
};
int main(int, char *[])
{
C c;
c.foo();
}
这编译并正常工作。
我还不完全确定这是一个错误。
<强>更新强>
所以这似乎是MSVC中缺少的功能。 MSVC(截至2015 / 14.0)似乎不支持&#34;两阶段名称查找&#34;。
Darran Rowe :VC还没有实现三个C ++ 98/03功能:两阶段 名称查找,动态异常规范和导出。两相 名称查找在2015年仍未实现,但它在编译器上 团队要做的事情列表,等待代码库现代化。动态 异常规范也未实现(VC给出 抛出()并忽略其他形式的非标准语义,但它们 在C ++ 11中被弃用了,现在没有人关心它们了 noexcept。我们不太可能实施它们,而且还有 甚至一直在谈论从C ++中删除它们17。最后,出口是 在C ++ 11中删除。
来源C++11/14/17 Features In VS 2015 RTM
2012年有一个错误请求该功能,但在没有评论的情况下关闭了该功能:support two-phase name lookup - by Ivan Sorokin
所以看起来你在这里正在做的一切,但是MSVC并不支持这部分C ++标准。