“如果 constexpr”与非安置新问题
Issue of `if constexpr` with non-placement new
我正在尝试编写一个实用程序,该实用程序根据T
是否为聚合类型来调用new T{...}
或new T(...)
。到目前为止,我所达到的如下。请注意,由于此问题,我使用的是宏而不是函数模板。
#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);
}
然后发生以下错误(实时(。
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
和放置 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__
而不是在函数中使用完美转发。在这里,编译器仍然尝试编译(扩展后(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);
}
}
如果在函数中使用非放置新功能与放置版本类似,则它有效:
#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';
}
输出:
11 2
0 3 4
这
完全是因为您尝试在宏中扩展__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)...);
}
}
相关文章:
- 如果我想在没有更新编译器的情况下使用新功能,该怎么办?
- 如果我真的真的想从 STL 容器继承,并且我继承构造函数并删除新运算符,会发生什么?
- C++线程安全:如果只有一个线程可以写入非原子变量,但多个线程从中读取. 会遇到问题吗?
- 基本的 c++ 问题:如果我在函数中创建某些内容并返回它会发生什么?
- 插入容器的容器(如果是新的或附加到现有容器)
- C++:如果我重载新运算符,我是否也必须重载删除运算符?
- 如果键不存在,使用 [] 运算符访问 STL Map 元素会添加新元素
- 如果我们不创建一个新节点并使用指针插入数据并建立链接(在链表中)怎么办?
- 如果我不使用"新"关键字,为什么会出现细分错误?
- 如果 GPA 计算器的语句问题
- Qt的新信号/时隙语法问题 - 连接到一个简单的函数
- “如果 constexpr”与非安置新问题
- R6010 执行后出错.if 语句在 for 循环中.[作业][中止已解决,发现新问题]
- 最后一行欧普特两个组合输出问题?如果其他语句
- C++输出错误(添加了新问题)
- C++中包含父类对象的子类有什么问题(如果有的话)
- C++,如果其他新文件出现问题,使用g++进行编译可以很好地工作
- C++重载了新问题
- 这个 C++11 可变参数和函数的实现有什么问题(如果有的话)?
- 创建一个新文件(如果不存在)