简体   繁体   中英

How does type deduction work for evaluating expressions?

I just did a clamp function using templates. It looks like this:

template <typename Tp_, typename Up_, typename Vp_>
inline auto clamp(Tp_ x, Up_ xmin, Vp_ xmax)
noexcept -> decltype(x < xmin ? xmin : (x > xmax ? xmax : x)) {
  return x < xmin ? xmin : (x > xmax ? xmax : x);
}

template <typename Tp_, typename Up_>
inline auto clamp(Tp_ x, const Up_ &v) -> decltype(clamp(x, min(v),
                                                         max(v))) {
  static_assert(is_iterable<Up_>::value,
                "The data set must be iterable");
  return clamp(x, min(v), max(v));
}

I have a unit test for testing that function that does this:

TEST(StatsTest, clamp) {
  ASSERT_EQ(clamp(1, 0, 5), 1);
  ASSERT_EQ(clamp(-1, 0, 3), 0);
  ASSERT_EQ(clamp(15, 0, 3), 3);

  ASSERT_EQ(clamp(1, 0.f, 3.), 1);
  ASSERT_EQ(clamp(-1, 0.f, 3.), 0.f);
  ASSERT_EQ(clamp(15, 0, 3), 3.);

  ASSERT_EQ(clamp(543, v1), 543);
  ASSERT_EQ(clamp(-143, v1), 71);
  ASSERT_EQ(clamp(14143, v1), 977);
}

But when I think about it, that doesn't make sense: the result of decltype(clamp(x, min(v), max(v))) is not supposed to be known before the run time, is it ?

Just in case, I've tried to replace my test with some variables datas:

TEST(StatsTest, clamp) {
  int a = 0;
  float b = 65.3f;
  double c = 89.7;
  ASSERT_EQ(clamp(a, b, c), 65.3f);
}

But it still passes !

What is the explanation ?

The type that clamp returns is the common type of the arguments, it is not the type of the argument that is selected.

You are then fooled by further promotions, making your equality checks hold.

decltype( true?7:3.0 ) is the same type as decltype( false?7:3.0 ) .

In general, you cannot do this. The only reason this works in this case is because you are working with integer/floating point values that have default conversions. For example:

float f;
int y;
double z;
std::cin >> f;
std::cin >> y;
std::cin >> z;
auto p = clamp(f, y, z);
static_assert(std::is_same<decltype(p), double>::value, "Not Double!");

This will work because all of the types given can be promoted to double . In general, if you tried to do this with types that do not have promotions like this, you would get a compile-time error, as the return type of a function must be known at compile time (of course it has to be, otherwise how would the compiler know how much space to allocate on the stack for the return value?).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM