Search code examples
c++std-variant

vector containing both string and string_view using std::variant


I have this code

#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;
}

I get the following error

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);

My understanding is that when I am doing std::vector({a,b}), its assuming the vector will have same types for all arguments which is understandable.

How to make it work with that std::vector({a,b})? How can I change the join() function or its just not possible because the failure itself is at construction of vector?


Solution

  • As mentioned in a comment, when the compiler sees std::vector({a, b}) it will try to deduce the template argument for std::vector, via CTAD, but a and b have conflicting types, so the deduction fails, as the error message is telling you.

    There's no reason why, because they are different, the compilier should deduce the template argument is a std::variant. Think about it: what if I had defined, in your code, another type, such as

    struct Foo {
        Foo(std::string);
        Foo(std::string_view);
    };
    

    This type, just like std::variant<std::string, std::string_view>, can be constructed from std::string and std::string_view. Should the compiler have deduced you wanted a std::vector<Foo>?

    Not to mention std::any, that can be constructed almost from everything. Should the compiler have deduced you wanted std::vector<std::any>?

    CTAD deduces template parameters from the arguments you pass to the class constructor, but it can't randomly pick conversions like the one you expect, so you have to be explicit:

    using VS = std::variant<std::string, std::string_view>; 
    // ...
      cout<<join(std::vector<VS>({a,b}))<<endl;