我可以检查表达式是否与类型相关吗?

Can I check if an expression is type-dependent?

提问人:HolyBlackCat 提问时间:11/12/2023 更新时间:11/12/2023 访问量:64

问:

有人告诉我答案是否定的,但我想我还是会问的。

我想在宏中检查特定表达式是否与类型相关。这里有一个激励人心的例子:

#include <concepts>
#include <format>
#include <iostream>

template <typename T>
void Print(const T &value)
{
    if constexpr (std::default_initializable<std::formatter<T>>)
        std::cout << std::format("{}", value) << '\n';
    else
        std::cout << "??\n";
}

#define PRINT(...) Print(__VA_ARGS__)

// ---

struct Unknown {};

template <typename T>
void MaybePrint(const T &value)
{
    PRINT(value);
}

int main()
{
    PRINT(42); // "42"
    PRINT(Unknown{}); // "??", but I want a build error, because the expression is not type-dependent.
    MaybePrint(42); // "42"
    MaybePrint(Unknown{}); // "??", this is ok, since the expression is type-dependent.
}

我有一个宏,可以打印传递给它的任何对象(这是用于单元测试框架的)。

如果它不可打印,我显然想要一个编译错误。但是对于类型相关的表达式,我想要打印一个占位符(以避免每次都强制用户写入)。if constexpr (printable<T>) ...

C++ 模板元编程

评论

0赞 Patrick Roberts 11/12/2023
在非依赖表达式上失败是硬错误,而不是 SFINAE,因为没有替换requires
0赞 Patrick Roberts 11/12/2023
但是,因为放在一个依赖的上下文中,所以你永远不会得到一个硬错误,除非有一个包含或格式错误的表达式的专用化。PrintTif constexprstd::formatterstatic_assert
0赞 HolyBlackCat 11/12/2023
@PatrickRoberts啊哈,所以移动到宏会给出所需的行为。你想把它作为答案发布吗?if constexpr
2赞 Sam Varshavchik 11/12/2023
如果这里的目标是调用站点的编译错误,那么创建一个真正的函数(以概念作为参数:)就足够了,而不是一个讨厌的宏。我刚刚测试了它,它有效。但是,不,没有办法让它成为宏观巫毒教。宏只是文本替换,仅此而已。PRINTtemplate<typename T> concept printable = std::default_initializable<std::formatter<T>>;
0赞 HolyBlackCat 11/12/2023
@SamVarshavchik 仅当用户尝试打印不可打印的非依赖表达式时,我才希望出现编译错误。如果它是依赖的,我不想要编译错误,那么我想打印一个占位符。

答:

2赞 Patrick Roberts 11/12/2023 #1

将 的主体直接移动到宏中并调整约束可提供所需的行为:Print

#define PRINT(...)                                                     \
  {                                                                    \
    if constexpr (requires {                                           \
                    std::formatter<                                    \
                        std::remove_cvref_t<decltype(__VA_ARGS__)>>{}; \
                  })                                                   \
      std::cout << std::format("{}", __VA_ARGS__) << '\n';             \
    else                                                               \
      std::cout << "??\n";                                             \
  }

struct Unknown {};

template <typename T>
void MaybePrint(const T &value) {
  PRINT(value);
}

https://godbolt.org/z/4Po3xh9oP

我从 Clang 中删除了它,因为它似乎缺乏对此示例的库支持,但关于硬错误与替换失败的基本概念在 Clang 中的工作方式与在 GCC 和 MSVC 中的工作方式相同。-stdlib=libc++

正如 @SamVarshavchik 所指出的,您还可以将约束移动到函数参数以获得所需的行为:

template <class T>
concept printable = std::default_initializable<std::formatter<T>>;

void Print(const printable auto &value) {
  std::cout << std::format("{}", value) << '\n';
}

struct Unknown {};

template <typename T>
void MaybePrint(const T &value) {
  if constexpr (printable<T>) {
    Print(value);
  }
}

https://godbolt.org/z/1MzWhKzrM

评论

0赞 HolyBlackCat 11/12/2023
我什至不需要改变条件,似乎对我来说是有效的。if constexpr
1赞 Patrick Roberts 11/12/2023
@HolyBlackCat 使用原始约束时,如果在非依赖上下文中失败,GCC 仍会传递:godbolt.org/z/Thboe83d6PRINT
1赞 HolyBlackCat 11/12/2023
啊哈,我明白了。关于 in - 这是我想避免的事情,因为我不希望库用户手动编写它。if constexprMaybePrintif constexpr
0赞 Patrick Roberts 11/12/2023
@HolyBlackCat啊,我只是想你可以提供一个 and 作为你图书馆的一部分。PrintMaybePrint