Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to overcome the MSVC bug of failing deduction of template argument of pointer to member method?

Below is a minimal code which compiles in g++, but gives error in MSVC:

template<typename Type,
         typename Return,  // <--- error: this is not deduced
         typename Container,
         typename Parameter>
Container
StringTo (Type&& copy,
          const char tokens[],
          Return (Container::*Insert) (const Parameter&))
{
  static_assert(not std::is_lvalue_reference<Type>::value, "Must be rvalue.");
  Container container;
  return container;
}

template<typename Type>
auto
StringToVector (Type&& copy,
                const char tokens[])
{
  static_assert(not std::is_lvalue_reference<Type>::value, "Must be rvalue.");
  return StringTo(std::move(copy), tokens, &std::vector<Type>::push_back); // <--- here
}

int main()
{
  auto v = StringToVector(std::string("hello world"), " ");
}

As per this post, it's a bug in MSVC which is not yet fixed: Visual Studio 2017 - could not deduce template argument (with variadic templates)

Question: What is the workaround to fix it for this specific case?


Update: This bug isn't fixable, & I am open to changing of design/interface. You are welcome to post it as an answer. Will accept the best one.

like image 669
iammilind Avatar asked Jan 31 '26 03:01

iammilind


2 Answers

By adding the container type you want to the template parameter list of StringTo and then taking the function as a generic type allows you to use a lambda in StringToVector to forward to the correct member function. That would look like

template<typename Container,
         typename Type,
         typename Func>
Container
StringTo (Type&& copy,
          const char tokens[],
          Func func)
{
  static_assert(not std::is_lvalue_reference<Type>::value, "Must be rvalue.");
  Container container;
  func(container, std::move(copy));
  return container;
}

template<typename Type>
auto
StringToVector (Type&& copy,
                const char tokens[])
{
  static_assert(not std::is_lvalue_reference<Type>::value, "Must be rvalue.");
  return StringTo<std::vector<Type>>(std::move(copy), tokens, [](auto& cont, auto&& val){ cont.push_back(std::move(val)); } ); // <--- here
}

int main()
{
  auto v = StringToVector(std::string("hello world"), " ");
}

And you can see it working on Rextester here: https://rextester.com/BLSS95194

like image 172
NathanOliver Avatar answered Feb 02 '26 17:02

NathanOliver


A more lean design might just do:

template<typename Type>
auto
StringToVector (Type&& copy,
                const char tokens[])//what are we going to use this for ?
{
  return std::vector{ copy };
}

Try it yourself on godbolt

Would that be a usable work-around ?


Another approach could be to use a type based customization design from the get-go instead (example using std::back_insert_iterator)

#include <utility>
#include <string>
#include <vector>

template<typename Container,
         typename Inserter,
         typename Type
         >
Container
StringTo (Type&& copy,
          const char tokens[])
{
  static_assert(!std::is_lvalue_reference<Type>::value, "Must be rvalue.");
  Container container;
  *Inserter(container) = copy;
  return container;
}

template<typename Type>
auto
StringToVector (Type&& copy,
                const char tokens[])
{
  static_assert(!std::is_lvalue_reference<Type>::value, "Must be rvalue.");
  return StringTo<std::vector<Type>, std::back_insert_iterator<std::vector<Type> > >(std::move(copy), tokens); 
}

int main()
{
  auto v = StringToVector(std::string("hello world"), " ");
}

Try it yourself

like image 30
darune Avatar answered Feb 02 '26 17:02

darune



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!