为什么这段代码在MSVC下能编译通过,但在GCC或Clang下不能?

5

如何修复这段代码?

这里是代码: https://godbolt.org/z/vcP6WKvG5

#include <memory>
#include <utility>

enum class Format {
    Number,
    Text,    
};

template <template <Format> typename Visitor, typename... Args>
void switchByFormat(Format format, Args&&... args) {
    switch(format) {
        case Format::Number: 
            Visitor<Format::Number>::visit(std::forward<Args>(args)...);
            break;
        case Format::Text: 
            Visitor<Format::Text>::visit(std::forward<Args>(args)...);
            break;
    }
}

struct AstNode {};

template <Format format>
struct ANode: public AstNode {};

using AstNodePtr = std::shared_ptr<AstNode>;

template <template <Format> typename AstNodeT, 
          typename... Args>
struct AstNodeFactory {
    template <Format format>
    struct Visitor {
        static void visit(AstNodePtr& result, Args&&... args)
        {
            result = std::make_shared<AstNodeT<format>>(std::forward<Args>(args)...);
        }
    };
};

template <template <Format> typename AstNodeT,              
          typename... Args>
AstNodePtr makeAstNode(Format format, Args&&... args)
{
    AstNodePtr result;
    switchByFormat<typename AstNodeFactory<AstNodeT, Args...>::Visitor>(format,
                                                                result,
                                                                std::forward<Args>(args)...);
    return result;
}

int main() {
    auto textAnode = makeAstNode<ANode>(Format::Text);
}

Clang链接:https://godbolt.org/z/9fxWE9j71

Clang编译器中的错误为:

source>:45:64: error: typename specifier refers to class template member in 'AstNodeFactory<ANode>'; argument deduction not allowed here
    switchByFormat<typename AstNodeFactory<AstNodeT, Args...>::Visitor>(format,

GCC链接:https://godbolt.org/z/4EvrzGWYE

GCC错误:

In instantiation of 'AstNodePtr makeAstNode(Format, Args&& ...) [with AstNodeT = ANode; Args = {}; AstNodePtr = std::shared_ptr<AstNode>]':
<source>:52:53:   required from here
<source>:45:5: error: 'typename AstNodeFactory<ANode>::Visitor' names 'template<enum class Format format> struct AstNodeFactory<ANode>::Visitor', which is not a type
   45 |     switchByFormat<typename AstNodeFactory<AstNodeT, Args...>::Visitor>(format,
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1个回答

4

由于Visitor是一个模板类而不是一种类型,您需要指定template关键字

switchByFormat<AstNodeFactory<AstNodeT, Args...>::template Visitor>(
                                                //^^^^^^^^
  format, result, std::forward<Args>(args)...);

演示。


1
OP 还在问为什么 MSVC 接受这段代码,以及它是否有效。OP 也添加了 [language-lawyer] 标签。 - cigien
哇,太简单了!非常感谢。是的,知道为什么MSVC在这里更宽松会很有趣。 - Alex Jenter
4
在历史上,MSVC执行两阶段名称查找的方式有所不同。因此,MSVC实际上根本不需要“typename”和“template”说明符:它已经“知道”给定标识符是指值、类型还是(如本例)模板。 - Konrad Rudolph
1
但是标准并不要求,在这种情况下只允许使用 template,对吗?因为没有命名任何特化。这难道不是 Davis Herring 在这里所说的 [https://dev59.com/hlEG5IYBdhLWcg3wNGjZ#70635817] 中也在 C++23 中被弃用的内容吗? - user17732522

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接