У меня есть этот код
#include <unistd.h>
#include <vector>
#include <iostream>
using namespace std;
std::string join(std::vector<std::variant<std::string, std::string_view>> input) {
return "";
}
int main(int argc, char* argv[]) {
string a = "hello";
string_view b = "world";
cout<<join(std::vector({a,b}))<<endl; // this returns error
vector<std::variant<string, string_view>> v;
v.push_back(a);
v.push_back(b);
cout<<join(v)<<endl; // this works
return 0;
}
Я получаю следующую ошибку
candidate template ignored: deduced conflicting types for parameter '_Tp' ('string' (aka 'basic_string<char>') vs. 'string_view' (aka 'basic_string_view<char>'))
vector(initializer_list<value_type> __il);
Насколько я понимаю, когда я делаю std::vector({a,b}), предполагается, что вектор будет иметь одинаковые типы для всех аргументов, что вполне понятно.
Как с этим заставить работать std::vector({a,b})? Как я могу изменить функцию join() или это просто невозможно, потому что сама ошибка связана с построением вектора?
Ожидать, что CTAD выявит использование std::variant только потому, что вы инициализируете vector двумя разными типами, — это слишком много.
хм, что это? моя функция соединения довольно проста...
Измените string a на variant<string, string_vew> a. Измените string_view b на variant<string, string_vew> b. Компилятор должен иметь возможность определить тип вектора как variant<string, string_vew>.





Как упоминалось в комментарии, когда компилятор видит std::vector({a, b}), он попытается вывести аргумент шаблона для std::vector через CTAD, но a и b имеют конфликтующие типы, поэтому вывод не удался, о чем сообщает сообщение об ошибке.
Нет причин, по которым, поскольку они разные, компилятор должен сделать вывод, что аргумент шаблона — это std::variant. Подумайте об этом: что, если бы я определил в вашем коде другой тип, например
struct Foo {
Foo(std::string);
Foo(std::string_view);
};
Этот тип, как и std::variant<std::string, std::string_view>, может быть составлен из std::string и std::string_view. Должен ли компилятор сделать вывод, что вам нужен std::vector<Foo>?
Не говоря уже о std::any, который можно построить практически из всего. Должен ли компилятор сделать вывод, что вы хотите std::vector<std::any>?
CTAD выводит параметры шаблона из аргументов, которые вы передаете конструктору класса, но он не может случайным образом выбирать преобразования, подобные тем, которые вы ожидаете, поэтому вам нужно указать явно:
using VS = std::variant<std::string, std::string_view>;
// ...
cout<<join(std::vector<VS>({a,b}))<<endl;
Вы можете получить более приятную функцию join, используя функцию шаблона с переменным числом аргументов, например:
#include <variant>
#include <vector>
#include <type_traits>
#include <iostream>
//using namespace std; <-- don't do this
using my_variant_t = std::variant<std::string, std::string_view>;
std::string join(std::vector<my_variant_t> input) {
return "";
}
// make a function template for join (that overloads your original join)
// the fold expression using td::is_constructible_v checks that ALL
// argument types can be used to construct your variant type
// std::enable_if enables the whole function (SFINAE) when this is true.
template<typename... args_t>
auto join(args_t&&... args) -> std::enable_if_t<((std::is_constructible_v<my_variant_t, args_t>), ...), std::string>
{
return "";
}
int main()
{
std::string a = "hello";
std::string_view b = "world";
// with the variadic function template
// your join syntax becomes even more user friendly
std::cout << join(a, b) << "\n";
std::vector<my_variant_t> v{a,b};
// this picks your original joim
std::cout << join(v) << "\n"; // don't use endl; // this works
return 0;
}
Поскольку join не является шаблонной функцией, это означает, что вы можете просто
string a = "hello";
string_view b = "world";
cout << join({a, b}) << endl;
который работает так, как вы и ожидали.
Просто не используйте CTAD в этом объявлении.