SFINAE:检测成员变量的存在对g ++不起作用

问题描述 投票:5回答:2

我正在尝试将this answer中使用的方法结合起来,用于检测类是否具有成员变量xthis answer,以根据使用enable_if选择不同的实现。

基本上,我想编写一个特征类,给定类型为T,如果存在,则提供对成员T::x的访问,否则提供默认值。

以下代码无法在g ++上编译:(Compiler Explorer

#include <iostream>
#include <type_traits>

// classes with / without x member
struct WithX { static constexpr int x = 42; };
struct WithoutX {};

// trait to detect x
template <typename T, typename = void>
struct HasX : std::false_type { };

template <typename T>
struct HasX <T, decltype((void) T::x)> : std::true_type { };

// trait to provide default for x
template <typename T>
struct FooTraits
{
    template <bool enable = HasX<T>::value>
    static constexpr std::enable_if_t< enable, size_t> x() { return T::x; }
    template <bool enable = HasX<T>::value>
    static constexpr std::enable_if_t<!enable, size_t> x() { return 1; }

};


int main() {
    std::cout << HasX<WithX>::value << std::endl;
    // Uncomment the following line to make this compile with g++
    //std::cout << HasX<WithoutX>::value << std::endl;
    std::cout << FooTraits<WithoutX>::x() << std::endl;
}

g ++给出了错误消息

error: 'x' is not a member of 'WithoutX'
  struct HasX <T, decltype((void) T::x)> : std::true_type { };

在应该检测x是否是第一名成员的部分。奇怪的是,如果我取消注释自己实例化HasX<WithoutX>::value的倒数第二行,g ++编译时没有错误(Compiler Explorer)。

clang和msvc在编译器资源管理器上编译都没有问题。

这有什么不对?

c++ g++ sfinae
2个回答
1
投票

转换评论的事实:

//std::cout << HasX<WithoutX>::value << std::endl;

确实是gcc bug的好兆头。

似乎gcc对您的表单有问题:

template <typename T>
struct HasX <T, decltype((void) T::x)> : std::true_type {};

更典型的方法是使用std::void_t

template <typename T>
struct HasX <T, std::void_t<decltype(T::x)>> : std::true_type {};

这确实解决了问题Demo


3
投票

SFINAE只能在紧急情况下工作。换句话说,如果编译器可以事先看到声明有问题,那么它必定是错误的。

实例化类时,编译器将尝试解决所有问题。这是这样的:

template<bool enable = HasX<T>::value>
....

这不取决于函数的上下文。这可以在FooTraits实例化时立即实例化。

换句话说,可以提前计算此分配,就像您将其移动到类范围一样。

在您的情况下,编译器与替换无关。

修复只是:

template <typename U = T, bool enable = HasX<U>::value>
static constexpr std::enable_if_t< enable, size_t> x() { return T::x; }
template <typename U = T, bool enable = HasX<U>::value>
static constexpr std::enable_if_t<!enable, size_t> x() { return 1; }

从理论上讲,U可能是一种完全不同的类型。 U是功能实例化的直接,而T不是。

© www.soinside.com 2019 - 2024. All rights reserved.