如何克服成员方法指针模板实参推导失败的MSVC bug?
How to overcome the MSVC bug of failing deduction of template argument of pointer to member method?
下面是一个最小的代码 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"), " ");
}
根据此 post,这是 MSVC 中的一个错误,尚未修复:Visual Studio 2017 - could not deduce template argument (with variadic templates)
问题:针对此特定情况,解决此问题的解决方法是什么?
更新:此错误无法修复,我愿意更改 design/interface。欢迎您将 post 作为答案。会接受最好的。
一个更精益的设计可能只是:
template<typename Type>
auto
StringToVector (Type&& copy,
const char tokens[])//what are we going to use this for ?
{
return std::vector{ copy };
}
这会是一个可用的解决方法吗?
另一种方法可能是从一开始就使用基于类型的定制设计
相反(例如使用 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"), " ");
}
通过将你想要的容器类型添加到StringTo
的模板参数列表中,然后将函数作为泛型类型允许你在StringToVector
中使用lambda来转发到正确的成员功能。看起来像
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"), " ");
}
您可以在此处看到它在 Rextester 上运行:https://rextester.com/BLSS95194
解决方法 1
将有问题的参数作为模板的第一个类型并显式传递。
template<typename Parameter,
typename Return,
typename Container>
Container
StringTo (Parameter&& copy,
const char tokens[],
Return (Container::*Insert) (const Parameter&))
{
Container container;
// do something ...
return container;
}
template<typename Type>
auto
StringToVector (Type&& copy,
const char tokens[])
{
return StringTo<Type>(std::move(copy), tokens, &std::vector<Type>::push_back);
} // ^^^^^^ here (explicit) ^^^^ passed anyways
解决方法 2
如果我们在 Type
部分妥协并直接使用 std::string
,那么这个错误在 MSVC 中也会消失。
template<typename Return,
typename Container>
Container
StringTo (std::string&& copy, // Type --> std::string
const char tokens[],
Return (Container::*Insert) (const std::string&))
{
Container container;
// do something ...
return container;
}
inline
auto
StringToVector (std::string&& copy,
const char tokens[])
{
return StringTo(std::move(copy), tokens, &std::vector<std::string>::push_back);
}
Demo.
下面是一个最小的代码 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"), " ");
}
根据此 post,这是 MSVC 中的一个错误,尚未修复:Visual Studio 2017 - could not deduce template argument (with variadic templates)
问题:针对此特定情况,解决此问题的解决方法是什么?
更新:此错误无法修复,我愿意更改 design/interface。欢迎您将 post 作为答案。会接受最好的。
一个更精益的设计可能只是:
template<typename Type>
auto
StringToVector (Type&& copy,
const char tokens[])//what are we going to use this for ?
{
return std::vector{ copy };
}
这会是一个可用的解决方法吗?
另一种方法可能是从一开始就使用基于类型的定制设计
相反(例如使用 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"), " ");
}
通过将你想要的容器类型添加到StringTo
的模板参数列表中,然后将函数作为泛型类型允许你在StringToVector
中使用lambda来转发到正确的成员功能。看起来像
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"), " ");
}
您可以在此处看到它在 Rextester 上运行:https://rextester.com/BLSS95194
解决方法 1
将有问题的参数作为模板的第一个类型并显式传递。
template<typename Parameter,
typename Return,
typename Container>
Container
StringTo (Parameter&& copy,
const char tokens[],
Return (Container::*Insert) (const Parameter&))
{
Container container;
// do something ...
return container;
}
template<typename Type>
auto
StringToVector (Type&& copy,
const char tokens[])
{
return StringTo<Type>(std::move(copy), tokens, &std::vector<Type>::push_back);
} // ^^^^^^ here (explicit) ^^^^ passed anyways
解决方法 2
如果我们在 Type
部分妥协并直接使用 std::string
,那么这个错误在 MSVC 中也会消失。
template<typename Return,
typename Container>
Container
StringTo (std::string&& copy, // Type --> std::string
const char tokens[],
Return (Container::*Insert) (const std::string&))
{
Container container;
// do something ...
return container;
}
inline
auto
StringToVector (std::string&& copy,
const char tokens[])
{
return StringTo(std::move(copy), tokens, &std::vector<std::string>::push_back);
}
Demo.