You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何以下C++代码可在MSVC2022编译却无法在GCC 12编译?

MSVC2022可编译但GCC12编译失败的C++模板代码原因分析

问题代码

#include <iostream>

template <auto value>
class getter;

template <class T, class ReturnType, ReturnType(T::* func_ptr)() const>
class getter<func_ptr>
{
public:

    using object_type = T;
    using value_type = ReturnType;

    constexpr ReturnType operator() (const T& val) const
    {
        return (val.*func_ptr)();
    }
};

template <class T, class Field, Field T::* field_ptr>
class getter<field_ptr>
{
public:

    using object_type = T;
    using value_type = Field;

    constexpr const Field& operator() (const T& val) const
    {
        return val.*field_ptr;
    }
};

struct A
{
    A() = default;

    explicit A(size_t k) : key(k), attribute(k + 1)
    {
    }

    size_t key;
    size_t attribute;

    size_t GetKey() const
    {
        return key;
    }

    const size_t & GetKeyRef() const
    {
        return key;
    }
};

int main()
{
    auto g1 = getter<&A::key>();
    auto g2 = getter<&A::GetKey>();

    A a(5);

    std::cout << g1(a) << " " << g2(a) << std::endl;

    return 0;
}

GCC12编译错误信息

MyTest.cpp: In function ‘int {anonymous}::main()’:
MyTest.cpp:66:38: error: ambiguous template instantiation for ‘class {anonymous}::getter<&{anonymous}::A::GetKey>’
   66 |         auto g2 = getter<&A::GetKey>();
      |                                      ^
MyTest.cpp:14:11: note: candidates are: ‘template<class T, class ReturnType, ReturnType (T::* func_ptr)() const> class {anonymous}::getter<func_ptr> [with T = {anonymous}::A; ReturnType = long unsigned int; ReturnType (T::* func_ptr)() const = &{anonymous}::A::GetKey]’
   14 |     class getter<func_ptr>
      |           ^~~~~~~~~~~~~~~~
MyTest.cpp:28:11: note:                 ‘template<class T, class Field, Field T::* field_ptr> class {anonymous}::getter<field_ptr> [with T = {anonymous}::A; Field = long unsigned int() const; Field T::* field_ptr = &{anonymous}::A::GetKey]’
   28 |     class getter<field_ptr>
      |           ^~~~~~~~~~~~~~~~~
MyTest.cpp:66:38: error: invalid use of incomplete type ‘class {anonymous}::getter<&{anonymous}::A::GetKey>’
   66 |         auto g2 = getter<&A::GetKey>();
      |                                      ^
MyTest.cpp:11:11: note: declaration of ‘class {anonymous}::getter<&{anonymous}::A::GetKey>’
   11 |     class getter;
      |           ^~~~~~
MyTest.cpp: At global scope:
MyTest.cpp:63:9: warning: ‘int {anonymous}::main()’ defined but not used [-Wunused-function]
   63 |     int main()
      |         ^~~~
ninja: build stopped: subcommand failed.

原因分析

GCC在模板参数推导时,会将成员函数指针&A::GetKey同时匹配到两个特化模板:

  • 第一个特化是专门针对成员函数指针ReturnType(T::*)() const的版本,完全匹配。
  • 第二个特化针对Field T::*(通用成员指针),GCC会将Field推导为成员函数类型size_t() const,此时Field T::*等价于成员函数指针类型size_t (A::*)() const,和&A::GetKey的类型完全一致,因此GCC认为这个特化也符合匹配条件,最终导致模板实例化出现歧义。

而MSVC的模板匹配逻辑更严格,它会区分成员对象指针和成员函数指针的特化,不会将成员函数指针匹配到针对成员对象指针的特化模板上,因此不会出现歧义。

解决方案

通过SFINAE机制约束第二个特化模板,仅当模板参数是成员对象指针时才生效,排除成员函数指针的情况。以下是两种实现方式:

C++20版本(推荐)

#include <iostream>
#include <type_traits>

template <auto value>
class getter;

template <class T, class ReturnType, ReturnType(T::* func_ptr)() const>
class getter<func_ptr>
{
public:
    using object_type = T;
    using value_type = ReturnType;

    constexpr ReturnType operator() (const T& val) const
    {
        return (val.*func_ptr)();
    }
};

template <class T, class Field, Field T::* field_ptr>
requires std::is_member_object_pointer_v<Field T::*>
class getter<field_ptr>
{
public:
    using object_type = T;
    using value_type = Field;

    constexpr const Field& operator() (const T& val) const
    {
        return val.*field_ptr;
    }
};

// 后续A结构体和main函数不变

C++17及之前版本

#include <iostream>
#include <type_traits>

template <auto value>
class getter;

template <class T, class ReturnType, ReturnType(T::* func_ptr)() const>
class getter<func_ptr>
{
public:
    using object_type = T;
    using value_type = ReturnType;

    constexpr ReturnType operator() (const T& val) const
    {
        return (val.*func_ptr)();
    }
};

template <class T, class Field, Field T::* field_ptr, 
          typename = std::enable_if_t<std::is_member_object_pointer_v<Field T::*>>>
class getter<field_ptr>
{
public:
    using object_type = T;
    using value_type = Field;

    constexpr const Field& operator() (const T& val) const
    {
        return val.*field_ptr;
    }
};

// 后续A结构体和main函数不变

添加约束后,GCC会排除成员函数指针对第二个特化的匹配,仅匹配第一个针对成员函数指针的特化,消除歧义,代码即可正常编译。

内容的提问来源于stack exchange,提问作者Alexey Starinsky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 04:59:52