Вектор, содержащий как строку, так и string_view, используя std::variant

У меня есть этот код

#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 в этом объявлении.

n. m. could be an AI 25.05.2024 07:43

Ожидать, что CTAD выявит использование std::variant только потому, что вы инициализируете vector двумя разными типами, — это слишком много.

wohlstad 25.05.2024 07:46

хм, что это? моя функция соединения довольно проста...

Shivanshu Arora 25.05.2024 07:47

Измените string a на variant<string, string_vew> a. Измените string_view b на variant<string, string_vew> b. Компилятор должен иметь возможность определить тип вектора как variant<string, string_vew>.

Eljay 25.05.2024 13:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
88
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Как упоминалось в комментарии, когда компилятор видит 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;

который работает так, как вы и ожидали.

Другие вопросы по теме