Почему использование SFINAE для определения существования метода не работает с std::vector::begin
Я ищу способ определить, есть ли у класса шаблона методы
begin
,
end
и
resize
.
Я попробовал модифицированную версию этого ответа:
#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;
}
Однако это печатает 0. Что забавно, так это то, что это будет работать с
cbegin
и
cend
но не с
begin
,
end
и
resize
. Однако определенные пользователем классы, реализующие эти методы, работают нормально.
Я пробовал это как с g++, так и с Visual Studio 19, и я получаю те же результаты, поэтому похоже, что это не связано с компилятором или реализацией STL.
1 ответ
std::vector
имеет переизбыток
begin
: одна перегрузка
const
а другой нет.
Компилятор не знает, какую перегрузку использовать, когда вы пишете
&C::begin
, поэтому он рассматривает эту неоднозначность как ошибку, которую обнаруживает SFINAE.
Вместо того, чтобы формировать указатель на
begin
просто назовите это:
// ...
template <typename C> static one test( decltype(void(std::declval<C &>().begin())) * );
// ...
(Если это не очевидно, если вы пытаетесь обнаружить функцию с параметрами, вы должны предоставить аргументы, например
.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;
}
А вот и модный C++20
requires
-основное решение:
#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;
}