加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 模板中的朋友功能(为什么这在Visual Studio中失败但在GCC和

发布时间:2020-12-16 10:08:03 所属栏目:百科 来源:网络整理
导读:鉴于以下内容(剥离和设计以突出问题): #include utilitynamespace Generic{ class Whatever { public: // ISSUE1 (member "swap()" has same name as non-member template) void swap() { } }; // Forward declaration of class template "Test" template c
鉴于以下内容(剥离和设计以突出问题):

#include <utility>

namespace Generic
{
    class Whatever
    {
    public:
        // ISSUE1 (member "swap()" has same name as non-member template)
        void swap()
        {
        }
    };

    // Forward declaration of class template "Test"
    template <class>
    class Test;

    // Prototype of function template "swap()" for class template "Test"
    template<class T>
    void swap(Test<T> &,Test<T> &);

    /////////////////////////////////////////////////////////////////
    // Class template "Test"
    /////////////////////////////////////////////////////////////////
    template <class T>
    class Test : public T
    {
    public:
        // Default constructor
        Test() = default;

        // Move constructor
        Test(Test &&test)
            : BaseClass(std::move(test))
        {
        }

        // Assignment operator
        Test& operator=(Test test)
        {
            // Note that the "Generic:" prefix here has no impact on the results
            Generic::swap(*this,test);

            return (*this);
        }

    private:
        using BaseClass = T;

        // ISSUE2 (qualified call - prefixing with "Generic:")
        friend void Generic::swap<T>(Test &,Test &);
    };

    /////////////////////////////////////////////////////////////////
    // Canonical swap function for class "Test" just above
    /////////////////////////////////////////////////////////////////
    template<class T>
    void swap(Test<T> &,Test<T> &)
    {
    }

} // namespace Generic

int main()
{
    using namespace Generic;
    Test<Whatever> test1;
    Test<Whatever> test2;
    test2 = std::move(test1);

    return 0;
}

任何人都可以看到它不应该编译的任何理由.请注意注释中的ISSUE1和ISSUE2.这些会影响结果. ISSUE1和ISSUE2有4种组合可以让事情发挥作用(见下表),虽然我知道另一种纠正方法,但这4种组合是我想要关注的.请注意,所有4种组合都在GCC和Clang中完全编译,但只有第4项在Visual Studio 2017(最新版本)中干净地编译.其他组合(1到3)失败,并在下面的RESULT列中看到错误(编译器引用了所有3种情况下ISSUE2行的问题):

ISSUE1                               ISSUE2                          RESULT
   ------                               ------                          ------
1) Leave unchanged                      Leave unchanged                 'Generic::Test': use of class template requires template argument list
                                                                        'Generic::swap': not a function

2) Leave unchanged                      Remove "Generic:" qualifier     syntax error: missing ';' before '<'
                                                                        'swap': illegal use of type 'void'
                                                                        'Generic::swap': 'friend' not permitted on data declarations
                                                                        'Generic::swap': redefinition; previous definition was 'function'
                                                                        unexpected token(s) preceding ';'

3) Comment out member "swap()"          Leave unchanged                 'Generic::Test': use of class template requires template argument list
                                                                        'Generic::swap': not a function

4) Comment out member "swap()"          Remove "Generic:" qualifier     Works!!!

是Visual Studio只是错误或GCC和Clang错了(前者似乎是基于我对合格/非限定名称查找规则,ADL等的理解).谢谢.

解决方法

嘿,找到了博客:
Two-phase name lookup support comes to MSVC.

一些相关的摘录:

The original design of templates for C++ meant to do exactly what the term “template” implied: a template would stamp out families of classes and functions. It allowed and encouraged,but did not require,early checking of non-dependent names. Consequently,identifiers didn’t need to be looked up during parsing of the template definition. Instead,compilers were allowed to delay name lookup until the template was instantiated. Similarly,the syntax of a template didn’t need to be validated until instantiation. Essentially,the meaning of a name used in a template was not determined until the template was instantiated.

In accordance with these original rules,previous versions of MSVC did very limited template parsing. In particular,function template bodies were not parsed at all until instantiation. The compiler recorded the body of a template as a stream of tokens that was replayed when it was needed during instantiation of a template where it might be a candidate.

然后,文章继续发布一些与n.m.’s minimal example非常相似的代码.

It’s important to note that overloads declared after the point of the template’s definition but before the point of the template’s instantiation are only considered if they are found through argument-dependent lookup. MSVC previously didn’t do argument-dependent lookup separately from ordinary,unqualified lookup so this change in behavior may be surprising.

最后,关于使MSVC使用更新的两阶段模板解析的注释:

You’ll need to use the 07002 to enable two-phase lookup in the MSVC compiler included with Visual Studio 2017 “15.3”. Two-phase name lookup drastically changes the meaning of some code so the feature is not enabled by default in the current version of MSVC.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读