在msvc上编译模板成员函数专门化的情况,而不是其他 [英] case of template member function specialization that compiles on msvc, not others

查看:342
本文介绍了在msvc上编译模板成员函数专门化的情况,而不是其他的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我将标题从工作更改为编译 out它不真正工作毕竟(感谢@bogdan的意见)。我在帖子末尾添加了代码,显示了为什么和如何。

[ EDIT ] I changed the title from works to compiles since it turns out that it doesn't truly work after all (thanks @bogdan for the comments). I added code at the end of the post showing why and how.

问题的第二部分仍然存在 - 是否有办法 em>吗?问题的关键是在基础模板< int N>中有一个虚函数 Observe 在从 X 派生的类中将类X 重新路由到模板化函数观察< N> $ c>,而不需要 X 中的任何支持代码。

The second part of the question still stands - is there a way to "fix" it? The crux of the matter is having a virtual function Observe in a base template<int N> class X be rerouted to a templated function Observe<N> in classes derived from X<N>, without requiring any supporting code in X.

有关如何通过要求 X 合作,请参阅此答案另一个问题基本上要求观察< N> 被声明到最导出的类中)。



查看这个问题选择哪个基类覆盖方法我发现下面的代码片段在 vc ++ 2015 (用 / W4 / Za )并返回预期输出,但未能在 gcc-5.1 clang 3.7 (在 ideone.com 试用)。

For an example of how it can be done by requiring X to cooperate see this answer to the other question (which basically requires that Observe<N> be declared into the most derived class).


While looking at this other question Choosing which base class to override method of I found out that the following snippet compiles cleanly on vc++ 2015 (with /W4 /Za) and returns the expected output, yet fails to compile on gcc-5.1 and clang 3.7 (tried at ideone.com).

我知道模板函数的专业化有许多陷阱,但我仍然好奇C ++标准的哪个字母适用于这种情况,以及 - 在可能的情况下,代码不完全兼容 - 是否有一个简单的方法来修复 。

I am aware that specialization of template functions has many pitfalls, but I am still curious which letter of the C++ standard applies in this case, and - in the likely case that the code is not fully compliant - whether there is an easy way to "fix" it.

#include <iostream>
using std::cout;
using std::endl;

typedef int Parameter;

class Observer
{
public:
    virtual void Observe(Parameter p) = 0;
};

class TaggedDispatch
{
public:
    template<size_t Tag> void TObserve(Parameter p);
};

template<size_t Tag>
class TaggedObserver : virtual public TaggedDispatch, public Observer 
{ 
public:
    virtual void Observe(Parameter p) override
    {   TObserve<Tag>(p); }
};

class Thing : public TaggedObserver<0>, TaggedObserver<11>
{   };

template<> void Thing::TObserve<0>(Parameter p)
{   cout << "Parent #  0, Parameter " << p << endl; }

template<> void Thing::TObserve<11>(Parameter p)
{   cout << "Parent # 11, Parameter " << p << endl; }

int main(int, char **)
{
    Thing test;
    test.TObserve<0>(101);
    test.TObserve<11>(999);

    return 0;
}

编译时输出 vc ++ 2015

Parent #  0, Parameter 101
Parent # 11, Parameter 999

gcc-5.1编译错误

prog.cpp:29:17: error: template-id 'TObserve<0>' for 'void Thing::TObserve(Parameter)' does not match any template declaration
 template<> void Thing::TObserve<0>(Parameter p)
                 ^
prog.cpp:32:17: error: template-id 'TObserve<11>' for 'void Thing::TObserve(Parameter)' does not match any template declaration
 template<> void Thing::TObserve<11>(Parameter p)

clang 3.7

Compile errors from clang 3.7.

prog.cpp:22:36: warning: 'override' keyword is a C++11 extension [-Wc++11-extensions]
        virtual void Observe(Parameter p) override
                                          ^
prog.cpp:29:24: error: no function template matches function template specialization 'TObserve'
template<> void Thing::TObserve<0>(Parameter p)
                       ^
prog.cpp:32:1: error: extraneous 'template<>' in declaration of variable 'TObserve'
template<> void Thing::TObserve<11>(Parameter p)
^~~~~~~~~~
prog.cpp:32:24: error: variable has incomplete type 'void'
template<> void Thing::TObserve<11>(Parameter p)
                       ^
prog.cpp:32:32: error: expected ';' at end of declaration
template<> void Thing::TObserve<11>(Parameter p)
                               ^
                               ;
prog.cpp:32:32: error: expected unqualified-id
1 warning and 5 errors generated.



真的工作在 vc ++ 2015 毕竟。似乎发生的是编译器允许 void Thing :: TObserve< 0> 定义,但是在内部将其映射到 void TaggedDispatch :: TObserve< 0> 。如果添加另一个派生类例如


[ EDIT ] It doesn't truly work in vc++ 2015 after all. What appears to happen is that the compiler allows the void Thing::TObserve<0> definition, but internally maps it to void TaggedDispatch::TObserve<0>. This becomes obvious if adding another derived class e.g.

class Other : public TaggedObserver<0>
{    };

template<> void Other::TObserve<0>(Parameter p)
{    cout << "Parent # 00, Parameter " << p << endl; }

然后, vc ++ 2015 错误消息:

error C2766: explicit specialization; 'void TaggedDispatch::TObserve<0>(Parameter)' has already been defined


推荐答案

MSVC错误接受代码; ang和GCC(和EDG)是正确的拒绝它。

MSVC is wrong to accept the code; Clang and GCC (and EDG) are right to reject it.

此案例与此问题中的类似,但它涉及一个不同的句法结构(和编译器中不同的代码路径,产生不同的标准一致性结果,只有EDG是一致的)。

This case is similar to the one in this question, but it involves a different syntactic construct (and different code paths in the compilers, yielding different Standard conformance results, with only EDG being consistent).

template<> void Thing :: TObserve< 0>(参数p) Thing :: TObserve< 0> declarator-id Thing :: nested-name-specifier 。 [8.3p1]说:

In template<> void Thing::TObserve<0>(Parameter p), Thing::TObserve<0> is a declarator-id, with Thing:: being a nested-name-specifier. [8.3p1] says:


[...]当 declarator-id 应将
引用到
限定符所引用的类或命名空间的先前声明的成员(或者在命名空间的情况下,是该命名空间的
内联命名空间集的元素(7.3.1))或专业化
;该成员不仅仅是由在
指定的类或命名空间范围内的
使用声明引入 nested-name-specifier declarator-id 。 [...]

[...] When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers (or, in the case of a namespace, of an element of the inline namespace set of that namespace (7.3.1)) or to a specialization thereof; the member shall not merely have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id. [...]

所以,你必须使用 template< void TaggedDispatch :: TObserve< 0> 。如问题所述,使用 Thing :: 可能会创建一个错误的印象,您可以提供 TObserve 对于不同的派生类,情况并非如此。只有一个 TObserve 成员函数模板,在 TaggedDispatch 中声明的模板,以及所有这样的显式专门化

So, you have to use template<> void TaggedDispatch::TObserve<0>. As noted in the question, using Thing:: could create the false impression that you can provide different explicit specializations of TObserve for different derived classes, which is not the case. There's only one TObserve member function template, the one declared in TaggedDispatch, and all such explicit specializations (and implicit or explicit instantiations, and partial specializations) are "attached" to that declaration.

一个解决方案,使事情按照你期望的方式工作是在每个派生的 Thing -like类中声明一个观察成员函数模板,可能提供相关< c $ c> Tag s,并且使用CRTP将模板的特化自动连接到相应的 Observer 接口实例: p>

One solution to make things work the way you expect is to declare an Observe member function template in each derived Thing-like class, possibly providing explicit specializations for relevant Tags if necessary, and let specializations of the template be automatically wired up to the corresponding Observer interface instance using CRTP:

#include <iostream>
#include <cstddef>

using Parameter = int;

struct Observer 
{
   virtual void Observe(Parameter p) = 0;
};

template<std::size_t Tag> struct TaggedObserver : Observer { };

template<class Derived, std::size_t Tag> struct CrtpObserver : TaggedObserver<Tag>
{
   void Observe(Parameter p) override
   {
      static_cast<Derived*>(this)->template Observe<Tag>(p);
   }
};

struct Thing : CrtpObserver<Thing, 0>, CrtpObserver<Thing, 1>
{
   template<std::size_t N> void Observe(Parameter p);
};

template<> void Thing::Observe<0>(Parameter p)
{
   std::cout << "Interface #0, Parameter " << p << '\n';
}

template<> void Thing::Observe<1>(Parameter p)
{
   std::cout << "Interface #1, Parameter " << p << '\n';
}

int main()
{
   Thing test;
   TaggedObserver<0>* p0 = &test;
   TaggedObserver<1>* p1 = &test;
   p0->Observe(7);
   p1->Observe(3);
}

这会将 Observer 接口在 Thing 它们属于哪里,而在每个派生类中需要最小的管道 - 没有什么比你要做的反正,如果你可以单独覆盖每个 Observer :: Observe

This places the implementations of the Observer interface in Thing where they belong, while requiring minimal plumbing in each derived class - not much more than what you would have to do anyway if you could separately override each Observer::Observe directly.

这篇关于在msvc上编译模板成员函数专门化的情况,而不是其他的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆