std::move const char* 完美转发
std::move on const char* with perfect forwarding
我在 MSVC v19.28
编译器(后来的版本修复了这个问题)上有一个有趣的问题,其中 const char*
被传递给可变参数模板 class 无法正确解析。如果 const char*
传递给可变参数模板 function 则没有错误。
为清楚起见,这里是代码:
#include <type_traits>
template <typename... T_Args>
struct Foo
{
template <typename T_Func>
void foo(T_Func func, T_Args&&... args)
{
func(std::forward<T_Args>(args)...);
}
};
template <typename T_Func, typename... T_Args>
void bar(T_Func func, T_Args&&... args)
{
func(std::forward<T_Args>(args)...);
}
int main()
{
bar([](int, float, const char* c){ }, 5, 5.0f, "Hello world");
// <source>(26): error C2672: 'Foo<int,float,const char *>::foo': no matching overloaded function found
// <source>(26): error C2440: 'initializing': cannot convert from 'const char [12]' to 'const char *&&'
// <source>(26): note: You cannot bind an lvalue to an rvalue reference
Foo<int, float, const char*> f;
f.foo([](int, float, const char* c){ }, 5, 5.0f, "Hello world");
// this compiles, but what are the repurcussions of std::move() on a string literal?
Foo<int, float, const char*> g;
g.foo([](int, float, const char* c){ }, 5, 5.0f, std::move("Hello world"));
}
因为我在一个大团队工作,所以我不建议升级 toolchain/compiler,所以我正在寻找解决方法,直到可以更新编译器。
解决方法之一是使用 std::move("Hello world")
。 std::move
在做什么 const char*
以及潜在的副作用是什么?
What is std::move
doing to a const char [12]
and what are the potential side-effects?
普通的数组到指针的隐式转换,和none。指针类型没有移动构造函数或移动赋值运算符,因此“移动”是(数组衰减到的指针的)副本。
旁白:我认为您的模板并不像您认为的那样。调用 Foo::foo
时不会推导包 T_Args...
,因此您没有通用引用,而是右值引用。
你是不是想说
template <typename... T_Args>
struct Foo
{
template <typename T_Func, typename... T_Args2>
void foo(T_Func func, T_Args2&&... args)
{
static_assert(std::is_constructible_v<T_Args, T_Args2> && ..., "Arguments must match parameters");
func(std::forward<T_Args2>(args)...);
}
};
或者可能更简单
struct Foo
{
template <typename T_Func, typename... T_Args>
void foo(T_Func func, T_Args&&... args)
{
func(std::forward<T_Args>(args)...);
}
};
这是 MSVC 错误。
参见 ticket, which is a Visual Studio 2019 16.9 Bug(已解决)。
Visual Studio 2019 16.9 使用 MSVC v19.28
(Wikipedia).
如果您打算使用通用引用来使用完美转发,请将 ...T_Args
从 class 移动到另一个答案所说的函数。
但是如果你打算在声明一个class时限制参数类型,你可以像下面这样写。
#include <type_traits>
#include <utility>
template<typename ...Args>
struct Foo {
template<typename F, typename ...Args2>
std::enable_if_t<(std::is_convertible_v<Args2, Args> && ...)>
foo(F func, Args2&&... args)
{
func(std::forward<Args2>(args)...);
}
};
int main() {
Foo<int, float, const char*> f;
f.foo([](int, float, const char* c){ }, 5, 5.0f, "Hello world");
// invalid type!
//f.foo([](int, float, const char* c){ }, 5, "", "Hello world");
return 0;
}
适用于 MSVC 19.28 link
或者只使用 std::is_invocable
.
我在 MSVC v19.28
编译器(后来的版本修复了这个问题)上有一个有趣的问题,其中 const char*
被传递给可变参数模板 class 无法正确解析。如果 const char*
传递给可变参数模板 function 则没有错误。
为清楚起见,这里是代码:
#include <type_traits>
template <typename... T_Args>
struct Foo
{
template <typename T_Func>
void foo(T_Func func, T_Args&&... args)
{
func(std::forward<T_Args>(args)...);
}
};
template <typename T_Func, typename... T_Args>
void bar(T_Func func, T_Args&&... args)
{
func(std::forward<T_Args>(args)...);
}
int main()
{
bar([](int, float, const char* c){ }, 5, 5.0f, "Hello world");
// <source>(26): error C2672: 'Foo<int,float,const char *>::foo': no matching overloaded function found
// <source>(26): error C2440: 'initializing': cannot convert from 'const char [12]' to 'const char *&&'
// <source>(26): note: You cannot bind an lvalue to an rvalue reference
Foo<int, float, const char*> f;
f.foo([](int, float, const char* c){ }, 5, 5.0f, "Hello world");
// this compiles, but what are the repurcussions of std::move() on a string literal?
Foo<int, float, const char*> g;
g.foo([](int, float, const char* c){ }, 5, 5.0f, std::move("Hello world"));
}
因为我在一个大团队工作,所以我不建议升级 toolchain/compiler,所以我正在寻找解决方法,直到可以更新编译器。
解决方法之一是使用 std::move("Hello world")
。 std::move
在做什么 const char*
以及潜在的副作用是什么?
What is
std::move
doing to aconst char [12]
and what are the potential side-effects?
普通的数组到指针的隐式转换,和none。指针类型没有移动构造函数或移动赋值运算符,因此“移动”是(数组衰减到的指针的)副本。
旁白:我认为您的模板并不像您认为的那样。调用 Foo::foo
时不会推导包 T_Args...
,因此您没有通用引用,而是右值引用。
你是不是想说
template <typename... T_Args>
struct Foo
{
template <typename T_Func, typename... T_Args2>
void foo(T_Func func, T_Args2&&... args)
{
static_assert(std::is_constructible_v<T_Args, T_Args2> && ..., "Arguments must match parameters");
func(std::forward<T_Args2>(args)...);
}
};
或者可能更简单
struct Foo
{
template <typename T_Func, typename... T_Args>
void foo(T_Func func, T_Args&&... args)
{
func(std::forward<T_Args>(args)...);
}
};
这是 MSVC 错误。
参见 ticket, which is a Visual Studio 2019 16.9 Bug(已解决)。
Visual Studio 2019 16.9 使用 MSVC v19.28
(Wikipedia).
如果您打算使用通用引用来使用完美转发,请将 ...T_Args
从 class 移动到另一个答案所说的函数。
但是如果你打算在声明一个class时限制参数类型,你可以像下面这样写。
#include <type_traits>
#include <utility>
template<typename ...Args>
struct Foo {
template<typename F, typename ...Args2>
std::enable_if_t<(std::is_convertible_v<Args2, Args> && ...)>
foo(F func, Args2&&... args)
{
func(std::forward<Args2>(args)...);
}
};
int main() {
Foo<int, float, const char*> f;
f.foo([](int, float, const char* c){ }, 5, 5.0f, "Hello world");
// invalid type!
//f.foo([](int, float, const char* c){ }, 5, "", "Hello world");
return 0;
}
适用于 MSVC 19.28 link
或者只使用 std::is_invocable
.