2

I've gotten some code in C++ that I am trying to uderstand, but there is one part that I just can't grasp, even though I've searched around about it on the Internet. My question is what this means:

if (!(T() < x))

In the struct:

struct Positive_Check_Except
{
    template<typename T>
    static bool validate(const T& x)
    {
    if (!(T() < x))
        throw check_error(std::to_string(x) + " not positive exception");
        return true;
    }
};
ThisAndThat
  • 110
  • 6
  • That is value-initalizing an object instance of type `T`, then firing the defined (and likely default) `operator <()` to determine if `x` is "greater" than that instantiated instance. Its basically assuming `T` is a non-class type (that therefore value-initializes to zero), Therefore it's testing that x is greater than the `0` equivalent of whatever `T`'s type is, and if x is above that, throw an exception. (and the name probably makes a lot more sense now). If you have access to the C++ standard, see sec 8.5/16 and follow the path to the description of value-initializaiton. – WhozCraig Aug 18 '13 at 09:18
  • One more note, it is hopefully obvious this will puke hard if `T` is a type that doesn't support default construction (such as a class-type without a default constructor) *and* a valid `operator <`. – WhozCraig Aug 18 '13 at 09:23
  • @WhozCraig *"Its basically assumming `T` is a non-class type (That therebefore value-initializes to zero)"* You are wrong. The code is not assumming anything. The type `T` could be a class type with a implicit conversion operator (`operator int()` for example) wich can return anything, not only zero. So the code could be not comparing `0 < x`. I think that template has been written with too many assumptions. – Manu343726 Aug 18 '13 at 09:24
  • @WhozCraig what Manu said. It could be an `std::string`, a container, any user defined type satisfying a couple of things. – juanchopanza Aug 18 '13 at 09:25
  • @juanchopanza a couple of those couple-of-things be in the followup i commented ? (and I understand Manu's comment, I always forget about cast-operators). – WhozCraig Aug 18 '13 at 09:26
  • @WhozCraig Yes, those would be the things (I also mentioned them in my answer). – juanchopanza Aug 18 '13 at 09:27
  • @juanchopanza one more thing, I know I could hammer this out myself but if you know the answer, please enlighten. if the object type does NOT support `operator <` but *does* have a cast operator to a type that *does*, is it (the cast operator, and its subsequent `operator <`) automatically invoked? And if it is, what if there is more than one? I would assume ambiguity, but am none-the-less curious. – WhozCraig Aug 18 '13 at 09:30
  • @WhozCraig my understanding is that the cast operator is automatically invoked for the comparison. And if there is an `operator<` that does not involve a conversion, that one wins, i.e. there is no ambiguity. – juanchopanza Aug 18 '13 at 09:34
  • @WhozCraig exactly, the operator is invoked. Is the same when you do a comparison between two value wrappers like `std::integral_constant`: `if( std::integral_constant<1>() < std::integral_constant<4>() )` for example. – Manu343726 Aug 18 '13 at 09:34
  • @Manu343726 (and juancho) so if there is more than one (say to an `int()` and a `float()`, it introduces an ambiguous conversion and is therefore an compile-time error? (and thanks to both of you for the enlightenment, btw). There is, after all, `operator <` for both `float` and `int`. – WhozCraig Aug 18 '13 at 09:36
  • 1
    @WhozCraig exactly, that would give an ambiguous overload. – juanchopanza Aug 18 '13 at 09:39
  • 1
    @juanchopanza thanks. I just also found out that if the conversion operator is not `const` it won't work regardless (didn't see that coming). Thanks to both of you. – WhozCraig Aug 18 '13 at 09:42

1 Answers1

4

Step by step:

  1. T() constructs a temporary, value-initialized instance of T.
  2. T() < x compares x, which is a T instance, with the temporary T() using less-than operator<.
  3. !(T() < x) negates the result of that comparison

It is checking that the argument x is greater than a value-initialized T, and throwing an exception if this is not the case.

It relies on T being a built-in type (in which case, value initialization is zero initialization), or a default constructible user defined type (in which case value initialization calls the default constructor). It also requires that an operator< exits that can compare two T instances and return something convertible to bool.

See here for more on value initialization.

Community
  • 1
  • 1
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • Thanks, together with WhozCraig's comment (" Its basically assuming T is a non-class type (that therefore value-initializes to zero), Therefore it's testing that X is greater than the 0 equivalent of whatever T's type is, and if x is above that,") I get it. – ThisAndThat Aug 18 '13 at 09:22
  • @ThisAndThat I wouldn't agree with that. I have edited my answer to explain the requirements on `T`. It doesn't have to be a non-class type. – juanchopanza Aug 18 '13 at 09:23
  • What if a user passes in a lambda for T? – James Aug 18 '13 at 09:49
  • @James: Then you'd get various compiler errors, since the lambda type doesn't meet the requirements of this template: it has a deleted default constructor and no overloads of `operator<` or `to_string`. – Mike Seymour Aug 18 '13 at 14:07