【问题标题】:Issue of `if constexpr` with non-placement new [duplicate]带有非放置新的“if constexpr”问题[重复]
【发布时间】:2018-04-06 07:48:00
【问题描述】:

我正在尝试编写一个实用程序,该实用程序根据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&lt;T&gt;为真时,不应该根本不考虑吗?

请注意,使用 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)...);
    }
}

我猜非放置版本有什么特别之处?

【问题讨论】:

  • 你为什么尝试在 C++ 中使用预处理器宏?如果您的 constexpr if 代码有效,请忘记所有宏的内容。
  • @Klaus 我已经更新了这个问题。我确实有理由不使用函数模板。
  • 哎呀,锤了。问题的症结等同于另一个问题(在模板外使用if constexpr),但也许应该在此处的答案中建立联系。
  • @Quentin 我已将其添加到我的答案中。

标签: c++ c++17 constexpr if-constexpr new-expression


【解决方案1】:

这与安置与非安置新无关。问题是由于您在宏中扩展 __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);
  }
}

如果您在函数中使用类似于放置版本的非放置 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

【讨论】:

  • 所以,它必须在函数模板中实现。
  • 我想是的,是的。
【解决方案2】:

这完全是因为您试图在宏中扩展__VA_ARGS__,而不是在模板中扩展std::forward&lt;Args&gt;(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)...);
    }
}

【讨论】:

    猜你喜欢
    • 2022-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-31
    • 2015-10-31
    • 2020-05-25
    • 1970-01-01
    • 2021-10-03
    相关资源
    最近更新 更多