带有非放置新的 `if constexpr` 问题
Issue of `if constexpr` with non-placement new
我正在尝试编写一个实用程序,根据 T
是否为聚合类型来调用 new T{...}
或 new T(...)
。到目前为止,我所达到的情况如下。请注意,由于 this issue.
,我使用的是宏而不是函数模板
#define MAKE(p, T, ...) \
T* p; \
if constexpr (::std::is_aggregate_v<T>) { \
p = new T{__VA_ARGS__}; \
} \
else { \
p = new T(__VA_ARGS__); \
}
我试图在 gcc 7.2.0 上用
测试它
struct pr_t {
int a, b;
};
int main() {
MAKE(p, pr_t, 1, 2);
}
然后发生以下错误(live)。
prog.cc: In function 'int main()':
prog.cc:9:26: error: new initializer expression list treated as compound expression [-fpermissive]
p = new T(__VA_ARGS__); \
^
prog.cc:17:3: note: in expansion of macro 'MAKE'
MAKE(p, pr_t, 1, 2);
^~~~
prog.cc:9:26: warning: left operand of comma operator has no effect [-Wunused-value]
p = new T(__VA_ARGS__); \
^
prog.cc:17:3: note: in expansion of macro 'MAKE'
MAKE(p, pr_t, 1, 2);
^~~~
prog.cc:9:26: error: no matching function for call to 'pr_t::pr_t(int)'
p = new T(__VA_ARGS__); \
^
prog.cc:17:3: note: in expansion of macro 'MAKE'
MAKE(p, pr_t, 1, 2);
^~~~
prog.cc:12:8: note: candidate: pr_t::pr_t()
struct pr_t {
^~~~
prog.cc:12:8: note: candidate expects 0 arguments, 1 provided
prog.cc:12:8: note: candidate: constexpr pr_t::pr_t(const pr_t&)
prog.cc:12:8: note: no known conversion for argument 1 from 'int' to 'const pr_t&'
prog.cc:12:8: note: candidate: constexpr pr_t::pr_t(pr_t&&)
prog.cc:12:8: note: no known conversion for argument 1 from 'int' to 'pr_t&&'
编译器谈到 p = new T(__VA_ARGS__);
有问题。但是当::std::is_aggregate_v<T>
为真时,不应该完全不考虑吗?
请注意,使用 if constexpr
和 placement new 的类似模式已经奏效。引用自 cppref 示例。
template<class T, class... Args>
T* construct(T* p, Args&&... args) {
if constexpr(std::is_aggregate_v<T>) {
return ::new (static_cast<void*>(p)) T{std::forward<Args>(args)...};
}
else {
return ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
}
}
我猜非放置版本有什么特别之处?
这完全是因为您试图在宏中展开 __VA_ARGS__
,而不是在模板中展开 std::forward<Args>(args)...
。
template<class T, class... Args>
T* make(Args&&... args) {
if constexpr(std::is_aggregate_v<T>) {
return ::new T{std::forward<Args>(args)...};
}
else {
return ::new T(std::forward<Args>(args)...);
}
}
这与放置与 non-placement 新无关。出现此问题的原因是您在宏中扩展 __VA_ARGS__
而不是在函数中使用完美转发。在这里,编译器仍然尝试编译(扩展后)p = new pr_t(1, 2);
,即使它永远不会被执行,然后失败。这样做的原因是(正如 Quentin 链接的副本中所指出的那样)由 if constexpr
引起的丢弃语句只是没有在封闭的模板实例化中实例化。作为一个简单的演示,请考虑这也会失败:
#include <iostream>
struct pr_t {
int a, b;
};
int main() {
if constexpr (false) {
pr_t* p = new pr_t(1, 2);
}
}
如果您在类似于放置版本的函数中使用 non-placement new,它会起作用:
#include <iostream>
#include <memory>
#include <type_traits>
template<class T, class... Args>
std::unique_ptr<T> makeFunc(Args&&... args) {
if constexpr (std::is_aggregate_v<T>) {
return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
else {
return std::make_unique<T>(std::forward<Args>(args)...);
}
}
struct Aggregate {
int a, b;
};
class No_Aggregate {
int a, b;
public:
explicit No_Aggregate(int a, int b) : a{a}, b{b} {}
auto get_a() {
return a;
}
auto get_b() {
return b;
}
};
int main() {
auto agg = makeFunc<Aggregate>(1, 2);
std::cout << std::is_aggregate_v<Aggregate> << ' '
<< agg->a << ' ' << agg->b << '\n';
auto n_agg = makeFunc<No_Aggregate>(3, 4);
std::cout << std::is_aggregate_v<No_Aggregate> << ' '
<< n_agg->get_a() << ' ' << n_agg->get_b() << '\n';
}
输出:
1 1 2
0 3 4
我正在尝试编写一个实用程序,根据 T
是否为聚合类型来调用 new T{...}
或 new T(...)
。到目前为止,我所达到的情况如下。请注意,由于 this issue.
#define MAKE(p, T, ...) \
T* p; \
if constexpr (::std::is_aggregate_v<T>) { \
p = new T{__VA_ARGS__}; \
} \
else { \
p = new T(__VA_ARGS__); \
}
我试图在 gcc 7.2.0 上用
测试它struct pr_t {
int a, b;
};
int main() {
MAKE(p, pr_t, 1, 2);
}
然后发生以下错误(live)。
prog.cc: In function 'int main()':
prog.cc:9:26: error: new initializer expression list treated as compound expression [-fpermissive]
p = new T(__VA_ARGS__); \
^
prog.cc:17:3: note: in expansion of macro 'MAKE'
MAKE(p, pr_t, 1, 2);
^~~~
prog.cc:9:26: warning: left operand of comma operator has no effect [-Wunused-value]
p = new T(__VA_ARGS__); \
^
prog.cc:17:3: note: in expansion of macro 'MAKE'
MAKE(p, pr_t, 1, 2);
^~~~
prog.cc:9:26: error: no matching function for call to 'pr_t::pr_t(int)'
p = new T(__VA_ARGS__); \
^
prog.cc:17:3: note: in expansion of macro 'MAKE'
MAKE(p, pr_t, 1, 2);
^~~~
prog.cc:12:8: note: candidate: pr_t::pr_t()
struct pr_t {
^~~~
prog.cc:12:8: note: candidate expects 0 arguments, 1 provided
prog.cc:12:8: note: candidate: constexpr pr_t::pr_t(const pr_t&)
prog.cc:12:8: note: no known conversion for argument 1 from 'int' to 'const pr_t&'
prog.cc:12:8: note: candidate: constexpr pr_t::pr_t(pr_t&&)
prog.cc:12:8: note: no known conversion for argument 1 from 'int' to 'pr_t&&'
编译器谈到 p = new T(__VA_ARGS__);
有问题。但是当::std::is_aggregate_v<T>
为真时,不应该完全不考虑吗?
请注意,使用 if constexpr
和 placement new 的类似模式已经奏效。引用自 cppref 示例。
template<class T, class... Args>
T* construct(T* p, Args&&... args) {
if constexpr(std::is_aggregate_v<T>) {
return ::new (static_cast<void*>(p)) T{std::forward<Args>(args)...};
}
else {
return ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
}
}
我猜非放置版本有什么特别之处?
这完全是因为您试图在宏中展开 __VA_ARGS__
,而不是在模板中展开 std::forward<Args>(args)...
。
template<class T, class... Args>
T* make(Args&&... args) {
if constexpr(std::is_aggregate_v<T>) {
return ::new T{std::forward<Args>(args)...};
}
else {
return ::new T(std::forward<Args>(args)...);
}
}
这与放置与 non-placement 新无关。出现此问题的原因是您在宏中扩展 __VA_ARGS__
而不是在函数中使用完美转发。在这里,编译器仍然尝试编译(扩展后)p = new pr_t(1, 2);
,即使它永远不会被执行,然后失败。这样做的原因是(正如 Quentin 链接的副本中所指出的那样)由 if constexpr
引起的丢弃语句只是没有在封闭的模板实例化中实例化。作为一个简单的演示,请考虑这也会失败:
#include <iostream>
struct pr_t {
int a, b;
};
int main() {
if constexpr (false) {
pr_t* p = new pr_t(1, 2);
}
}
如果您在类似于放置版本的函数中使用 non-placement new,它会起作用:
#include <iostream>
#include <memory>
#include <type_traits>
template<class T, class... Args>
std::unique_ptr<T> makeFunc(Args&&... args) {
if constexpr (std::is_aggregate_v<T>) {
return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
else {
return std::make_unique<T>(std::forward<Args>(args)...);
}
}
struct Aggregate {
int a, b;
};
class No_Aggregate {
int a, b;
public:
explicit No_Aggregate(int a, int b) : a{a}, b{b} {}
auto get_a() {
return a;
}
auto get_b() {
return b;
}
};
int main() {
auto agg = makeFunc<Aggregate>(1, 2);
std::cout << std::is_aggregate_v<Aggregate> << ' '
<< agg->a << ' ' << agg->b << '\n';
auto n_agg = makeFunc<No_Aggregate>(3, 4);
std::cout << std::is_aggregate_v<No_Aggregate> << ' '
<< n_agg->get_a() << ' ' << n_agg->get_b() << '\n';
}
输出:
1 1 2
0 3 4