-2

I have a class that represents a quantity that can only take on integer or half-integer values, and so stores its value internally as an integer that is twice the value. That is: a value of 0.5 is stored as 1, 1 as 2, 1.5 as 3, etc. Let's call it class A with a stored value int TwoA_.

This forces the A values to be properly integer or half-integer. Since I often need to use them as their true values, I have naturally made a cast to double operator that simply returns 0.5 * TwoA_.

However, very frequently I need to use twice the value. I thought it would be neat to code 2 * a (a being an object of type A) to directly return the value TwoA_ stored inside a.

Ideally, I'd like to make an operator that only does this when multiplied by 2 from the left. All other situations would be covered by the implicit cast.

Is there any way to do this in c++? I though of some sort of templated multiplication operator.

The simple solution of just defining

friend double operator*(int lhs, const A& rhs)
{ return (lhs == 2) ? rhs.TwoA_ : lhs * static_cast<double>(rhs); }

is not what I'm looking for. 2 * a is guaranteed to be an int (but generically an_int * a is not), and I would like to return an int.

This is a question of curiosity; if you only want to comment "why would you want to do that?" (or some variation thereof), please hold your tongue.

An edit: I meant multiplication on the left by the integer literal 2 (not an integer variable which is tested to see if it has the value 2)

DanielGr
  • 191
  • 1
  • 6
  • 5
    Sorry, `2 * a` and `3 * a` cannot have different types. – aschepler Aug 23 '16 at 15:44
  • @aschepler: Yeah, different types would only be possible with template arguments, e.g. `a.scaleby<2>()` can have a different type from `a.scaleby<3>()` – Ben Voigt Aug 23 '16 at 15:46
  • @aschepler, Your answer is completely unhelpful unless you provide a reason. – DanielGr Aug 23 '16 at 15:46
  • @DanielGr: Is this a language-lawyer reason where you want a quote from the Standard? Or is it enough to know that **overload resolution uses argument types but not argument values**? – Ben Voigt Aug 23 '16 at 15:48
  • @BenVoigt, yeah... I thought of typedeffing a std::ratio and somehow building a doubler out of it. – DanielGr Aug 23 '16 at 15:48
  • @benVoigt, a bit of both: I understand what he wrote and that was of course my first thought. I thought there might be someway to use a deduced template argument in combination with enable_if to achieve the limit to the first argument being `2`. – DanielGr Aug 23 '16 at 15:49
  • 2
    Please note that you're wasting your time -- double can store half-integers precisely with no representation error (until you get well beyond `INT_MAX` anyway) and if your conversion is visible to the compiler, it will inline it and remove the redundant multiplies anyway. – Ben Voigt Aug 23 '16 at 15:49
  • @DanielGr: Template argument deduction also works on argument types, not values (non-type template arguments can be deduced, but only from the actual parameter's type, its value is not considered during deduction) – Ben Voigt Aug 23 '16 at 15:51
  • @BenVoigt, while doubles can store half integers, they can also store quarter integers, tenth integers, etc. I want to store *only* integers and half-integers and make it impossible (via compile-time errors) to store anything otherwise. – DanielGr Aug 23 '16 at 15:54
  • Re: "covered by the implicit cast" - there is no such thing as an implicit cast. A cast is something you write in your source code to tell the compiler to do a conversion. So a cast is **always** explicit, while a conversion can be explicit or implicit. – Pete Becker Aug 23 '16 at 15:55

2 Answers2

3

Understanding your question to be if you can overload the * operator such that it returns a different data type based on the value of one of the parameters, the short answer is: No.

The long answer: No, but maybe yes.

double operator*(const int lhs, const A& rhs);

That operator would have to know AT COMPILE TIME if lhs is 2 or not, and that cannot be guaranteed. Which means a decision as to how to handle the case of lhs being 2 or not must be done at runtime. But the data type that an operator returns must be known at compile time...

double operator*(const some_type_1& lhs, const A& rhs);
int operator*(const some_type_2& lhs, const A& rhs);

This would get you two different return types, but only with compile-time knowledge of what those argument types are.

template<int T>
struct special_number{};
//...
template<>
int operator*(const special_number<2>&, const A& rhs){
  return rhs.TwoA_;
}

template<int T>
double operator*(const special_number<T>&, const A& rhs){
  return (static_cast<double>(rhs) / 2.0) * T;
}

//usage
int answer1 = special_number<2> * my_a_object;
double answer2 = special_number<3> * my_a_object;

This would do just that.

Altainia
  • 1,347
  • 1
  • 7
  • 11
  • Would I be mistaken to suggest std::integral_constant instead of special_number here? – John P Aug 23 '16 at 22:10
  • I guess I should have been a little clearer in my original question. I don't want to cover the situation of `x * a` where `x` happens to have the value 2. I meant multiplication on the left by the literal 2. I thought possibly using something like `template auto operator*(int N, A a) -> typename std::enable_if::type` --- I understand that's missing something, but thought it might get along the way somehow. – DanielGr Aug 23 '16 at 23:15
  • Nah, your `N` must be known at compile time and must be guaranteed to be known at compile time for that to work. An argument passed to a function can be known at compile time but can also be dynamic to runtime. Using something like demonstrated (or std::integral_constant, as John P mentions) would get you compile-time knowledge of which overloaded function to use, but would be overkill in my opinion for most cases. – Altainia Aug 24 '16 at 13:34
  • Also bear in mind that compiler optimizations would likely reduce your original post's runtime check completely when it does see a constant `2` being used. I realize this is a question more for a conceptual "how can it be done if at all" and the general pattern can be accomplished, but in my experience, if there's no good generic use-case, it's probably not worth it and if there is a contrived use case there's probably a better, more specific solution just for that. – Altainia Aug 24 '16 at 13:42
  • @Altainia, I want it to be known at compile time. But yeah, the complicated suggestions that involve creating a new structure to represent the literal 2 are overkill---if I were to have something other than `2 * a`, why not just add a method `twice(a)`? And yes: it's a purely conceptual question. – DanielGr Aug 24 '16 at 18:36
1
struct two_t {
  constexpr two_t() {};
  template<class T>
  constexpr operator T()const{return 2;}
};
constexpr two_t two;

Now:

friend double operator*(two_t lhs, const A& rhs)
{ return rhs.TwoA_; }
friend double operator*(const A& lhs, two_t lhs)
{ return lhs.TwoA_; }

is about the best you can do.

The type of an expression is not dependent on the values, except in the special case where the constant 0 can be converted to a pointer and can cause different overload resolutions.

That special case cannot be leveraged to make 2 special when passed as an argment to *.

Now

constexpr std::ptrdiff_t from_digits(std::integer_sequence<char>) {
  return 0;
}
constexpr std::ptrdiff_t compile_time_pow( std::ptrdiff_t base, std::size_t exp ) {
  return exp ? base * compile_time_pow( base, exp-1 ) : std::ptrdiff_t(1);
}
template<char c0, char...cs>
constexpr std::ptrdiff_t from_digits(std::integer_sequence<char, c0, cs...>) {
  return
    compile_time_pow(10, sizeof...(cs))*(c0-'0')
    + from_digits( std::integer_sequence<char, cs...>{} );
}
template<char...cs>
constexpr auto operator"" _integer() {
  return std::integral_constant<std::ptrdiff_t, from_digits( std::integer_sequence<char, cs...>{} )>{};
}

Now 2_integer is a compile_time std::ptrdiff_t of value 2, with the value encoded in its type.

Thus 2_integer * a could be overridden to return a differnet type than 3_integer * a, or 2 * a.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524