2

I have done a bit of browsing and this was the most relevant link that I could find, however it does not answer my question

Question: Why does the template substitution fail and the following does not compile?

template <typename T>
struct A
{
   A() {};
   A(T value) : val(value){} 
   operator T() { return this->val;}
   T val;
};


A<std::string> test;
std::cout << "xxx" + std::string(test); //works fine
std::cout << "xxx" + test; //compiler error

Error message:

error: no match for 'operator+' (operand types are 'const char [4]' and 'A<std::__cxx11::basic_string<char> >')
   19 |    std::cout << "xxx" + test;
      |                 ~~~~~ ^ ~~~~
      |                 |       |
      |                 |       A<std::__cxx11::basic_string<char> >
      |                 const char [4]
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
eucristian
  • 391
  • 3
  • 17
  • 1
    it is not substitution that fails. Please include the error message in the question – 463035818_is_not_an_ai Aug 18 '21 at 09:20
  • 1
    Fwiw, `static_cast` would work here, because it considers not only implicit conversion (which will fail here), but also direct-initialization *and* conversion functions (which will work here). I.e. `"xxx" + static_cast(test)` *should* succeed. – WhozCraig Aug 18 '21 at 09:26
  • thank you @WhozCraig . Would there be an way to fix the template so that it can accommodate for this situation? – eucristian Aug 18 '21 at 09:28
  • 1
    @eucristian Meh. Not really; at least nothing I would want to put in code with my name on it. A template friend overload for `operator +` combined with a friend overload of `operator <<` would do it, but good lord yuck. The latter is avoidable with the method shown by @songyuanyao in his answer comment. – WhozCraig Aug 18 '21 at 09:40

1 Answers1

4

std::operator+(std::basic_string) is a set of operator templates, template argument deduction needs to be performed on the 2nd operand test. But implicit conversion (from A<std::string> to std::string) won't be considered in template argument deduction.

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

As you have showed, explicit conversion like "xxx" + std::string(test); works fine. You can also specify template arguments explicitly (in ugly way) to bypass template argument deduction.

operator+ <char, std::char_traits<char>, std::allocator<char>>("xxx", test);
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Thank you for this. any good techniques/suggestion to correct the template to accommodate for this? – eucristian Aug 18 '21 at 09:24
  • @eucristian I think it would be to define `operator+` for `A` directly. – songyuanyao Aug 18 '21 at 09:27
  • 1
    @eucristian There is no real correct way to do this. You can write an operator that would take "anything that can be converted to std::string" + "anything that can be converted to std::string", but that would create other issues of ambiguity quickly. `std::string{"xxx"} + test` will also work, but to ask the compiler to convert both arguments to find a match is generally too greedy of an approach. – super Aug 18 '21 at 09:29
  • 1
    @super [`std::string{"xxx"} + test`](https://wandbox.org/permlink/jWLyUeVSr5hCnRAo) still won't work since template argument deduction on `test` fails. – songyuanyao Aug 18 '21 at 09:31
  • @songyuanyao Oh, right. I somehow assumed there was a non-template `operator+` somewhere for same-kind-of `std::string`. – super Aug 18 '21 at 09:35
  • 1
    @eucristian e.g. [this](https://wandbox.org/permlink/FcnzpRl1ouRLcY48). – songyuanyao Aug 18 '21 at 09:36
  • thank you for the code snippet @songyuanyao, however this takes me down a rabbit hole that I would like to try and avoid, as there would soon be other operators that I would need to overload and so on. But thank you, nontheless – eucristian Aug 18 '21 at 09:44