1

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?

4
  • Just don't use CTAD in that declaration. Commented May 25, 2024 at 5:43
  • 2
    Expecting CTAD to duduce the usage of std::variant just because you initialize the vector with 2 different types is a bit too much. Commented May 25, 2024 at 5:46
  • hmm, what is that? my join function is pretty simple... Commented May 25, 2024 at 5:47
  • Change string a to variant<string, string_vew> a. Change string_view b to variant<string, string_vew> b. The the compiler should be able to deduce the type of vector as variant<string, string_vew>. Commented May 25, 2024 at 11:39

3 Answers 3

3

Since join is not a template function, this means that you can just

string a = "hello";
string_view b = "world";
cout << join({a, b}) << endl;

which works as you expected.

Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

0

You can get a nicer join function by using a variadic template function like this :

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.