Код:
#include <iostream>
#include <string>
#include <vector>
#include <variant>
struct ignore
{
template <typename T>
void operator()([[maybe_unused]]const T&)
{
std::cout << "other type" << std::endl;
}
};
template <class... Ts>
struct overloaded_ignore : ignore, Ts...
{
overloaded_ignore(Ts...) : ignore(){}
using Ts::operator()...;
using ignore::operator();
};
int main()
{
std::variant<int,float,std::string> my_var = std::string("helloworld");
// std::variant<int,float,std::string> my_var = 5.0F;
// std::variant<int,float,std::string> my_var = 3;
std::visit(overloaded_ignore{
[](const int& t)
{
std::cout << "int var: " << t << std::endl;
},
[](const std::string& t)
{
std::cout << "string var: " << t << std::endl;
}
}, my_var);
return 0;
}
Я ожидаю вывести «строку var: helloworld», но выводится «другой тип».
Как это исправить?
Примечание: игнорируемый operator()
обязателен.
Просто добавьте квалификатор const
к ignore::operator()
, поскольку лямбда operator()
по умолчанию является const
-квалификатором:
struct ignore
{
template <typename T>
void operator()([[maybe_unused]]const T&) const // <--
{
std::cout << "other type" << std::endl;
}
};
template <class... Ts>
struct overloaded_ignore : ignore, Ts...
{
overloaded_ignore(Ts...) : ignore(){}
using Ts::operator()...;
using ignore::operator();
};
(Поскольку overloaded_ignore{...}
является ценным значением, это делает не-const
ignore::operator()
лучшим совпадением раньше)
оно работает. Вы меня очень удивили, поскольку решили эту проблему таким простым способом. большое спасибо.
да, на самом деле вначале я действительно оставляю оператор() в перегруженном_игноре, как вы сказали, но без квалификатора const, поэтому я не могу получить правильный вывод, поэтому я перемещаю его в новую структуру. я думал, поскольку "Ts" (который является лямбда-выражением в списке аргументов конструктора) является фиксированным типом, поэтому он должен лучше соответствовать шаблону <T> void ignore::operator()(auto&t); но это не так.
не могли бы вы объяснить, почему ignore::operator() лучше подходит, чем лямбда-выражение в моей реализации, даже объект перегрузки_ignore является значением prvalue, но зачем при компиляции сначала выбирать ignore::operator()? @ДжеДжо
@anti-walker Потому что вы вызываете operator()
из overloaded_ignore{...}
, что является значением prvalue, а не константным объектом. Если вы создадите константный overloaded_ignore
объект, то лямбда будет лучше соответствовать, если вы вызовете его operator()
.
ок, я понял, оператор(), сгенерированный лямбда-выражением, по умолчанию является константным, раньше я этого не осознавал. поэтому для неконстантного объекта класса перегрузки_ignore лучше подходит неконстантный квалифицированный оператор(). еще раз спасибо @康桓瑋
Если вы переместите оператор() в
overloaded_ignore
, вы также можете сохранить конструктор и один изusing ...
: godbolt.org/z/nThvYaosr