为什么使用 SFINAE 查找方法是否存在会失败并使用 std::vector::begin

Tom*_*myD 25 c++ sfinae

我正在寻找一种方法来检测模板类是否具有方法begin,endresize.

我尝试了这个答案的修改版本:

#include <iostream>
#include <vector>

// SFINAE test
template <typename T>
class has_method
{
    typedef char one;
    struct two { char x[2]; };

    template <typename C> static one test( decltype(&C::begin) ) ;
    template <typename C> static two test(...);    

public:
    enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

int main(int argc, char *argv[])
{
    std::cout << has_method<std::vector<int>>::value << std::endl;
    
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

然而,这会打印 0。有趣的是,这将适用于cbeginandcend但不适用于begin, endand resize。不过,实现这些方法的用户定义类工作正常。

我已经用 g++ 和 Visual Studio 19 尝试过这个,我得到了相同的结果,所以这似乎与编译器或 STL 的实现无关。

Hol*_*Cat 33

std::vector有一个超载begin:一个是超载const,另一个不是。

编译器不知道在您编写时使用哪个重载&C::begin,因此它将这种歧义视为错误,由 SFINAE 检测到。

而不是形成指向 的指针begin,只需调用它:

// ...
template <typename C> static one test( decltype(void(std::declval<C &>().begin())) * );
// ...
Run Code Online (Sandbox Code Playgroud)

(如果它不明显,如果您尝试检测带有参数的函数,则必须提供参数,例如.resize(0)(或可能.resize(std::size_t{}))而不是仅提供.resize().)

这是另一个更短的解决方案:

#include <iostream>
#include <vector>

template <typename T, typename = void>
struct has_begin : std::false_type {};

template <typename T>
struct has_begin<T, decltype(void(std::declval<T &>().begin()))> : std::true_type {};

int main(int argc, char *argv[])
{
    std::cout << has_begin<std::vector<int>>::value << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

这是一个requires基于C++20的奇特解决方案:

#include <iostream>
#include <vector>

template <typename T>
concept has_begin = requires(T t) {t.begin();};

int main(int argc, char *argv[])
{
    std::cout << has_begin<std::vector<int>> << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

  • @TommyD 我建议发布一个新答案(如果还没有人展示这些方法)。或者至少修改原来的答案而不是完全替换代码。 (4认同)
  • @idclev463035818 那么,结果类型必须是“void”,以匹配默认参数。我可以用 `std::void_t&lt;decltype(blahblah.begin())&gt;` 来实现相同的效果,但是 Clang 有时 [用它做一些奇怪的事情](/sf/ask/3216429331/ /2752075),所以我避免它。 (2认同)
  • 您的版本也更好,因为如果您采用标准库函数的地址,则未指定会发生什么(包括使您的程序格式错误):http://eel.is/c++draft/constraints#namespace.std- 6 (2认同)