We consider the goal of creating two different types, using the exact same syntax. This can be easily done with lambdas:
auto x = []{};
auto y = []{};
static_assert(!std::is_same_v<decltype(x), decltype(y)>);
But instead of using lambdas, we are looking for another, more elegant syntax. Here are some tests. We start by defining some tools:
#include <iostream>
#include <type_traits>
#define macro object<decltype([]{})>
#define singleton object<decltype([]{})>
constexpr auto function() noexcept
{
return []{};
}
template <class T = decltype([]{})>
constexpr auto defaulted(T arg = {}) noexcept
{
return arg;
}
template <class T = decltype([]{})>
struct object
{
constexpr object() noexcept {}
};
template <class T>
struct ctad
{
template <class... Args>
constexpr ctad(const Args&...) noexcept {}
};
template <class... Args>
ctad(const Args&...) -> ctad<decltype([]{})>;
and the following variables:
// Lambdas
constexpr auto x0 = []{};
constexpr auto y0 = []{};
constexpr bool ok0 = !std::is_same_v<decltype(x0), decltype(y0)>;
// Function
constexpr auto x1 = function();
constexpr auto y1 = function();
constexpr bool ok1 = !std::is_same_v<decltype(x1), decltype(y1)>;
// Defaulted
constexpr auto x2 = defaulted();
constexpr auto y2 = defaulted();
constexpr bool ok2 = !std::is_same_v<decltype(x2), decltype(y2)>;
// Object
constexpr auto x3 = object();
constexpr auto y3 = object();
constexpr bool ok3 = !std::is_same_v<decltype(x3), decltype(y3)>;
// Ctad
constexpr auto x4 = ctad();
constexpr auto y4 = ctad();
constexpr bool ok4 = !std::is_same_v<decltype(x4), decltype(y4)>;
// Macro
constexpr auto x5 = macro();
constexpr auto y5 = macro();
constexpr bool ok5 = !std::is_same_v<decltype(x5), decltype(y5)>;
// Singleton
constexpr singleton x6;
constexpr singleton y6;
constexpr bool ok6 = !std::is_same_v<decltype(x6), decltype(y6)>;
and the following test:
int main(int argc, char* argv[])
{
// Assertions
static_assert(ok0); // lambdas
//static_assert(ok1); // function
static_assert(ok2); // defaulted function
static_assert(ok3); // defaulted class
//static_assert(ok4); // CTAD
static_assert(ok5); // macro
static_assert(ok6); // singleton (macro also)
// Display
std::cout << ok1 << std::endl;
std::cout << ok2 << std::endl;
std::cout << ok3 << std::endl;
std::cout << ok4 << std::endl;
std::cout << ok5 << std::endl;
std::cout << ok6 << std::endl;
// Return
return 0;
}
this is compiled with the current trunk version of GCC, with options -std=c++2a
. See result here in compiler explorer.
The fact that ok0
, ok5
and ok6
work are not really a surprise. However, the fact that ok2
and ok3
are true
while ok4
is not is very surprising to me.
- Could someone provide an explanation of the rules that make
ok3
true
but ok4
false
?
- Is it really how it should work, or this is a compiler bug concerning an experimental feature (lambdas in unevaluated contexts)? (reference to the standard or to C++ proposals are very welcomed)
Note: I really hope this is a feature and not a bug, but just because it makes some crazy ideas implementable
question from:
https://stackoverflow.com/questions/55475338/default-template-parameter-lambda-in-unevaluated-context-bug-or-feature 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…