3

I have situations where sometimes based on some bool I want to call 2 constexpr functions that return different types and assign it to auto constant.

Unfortunately ternary operator needs types to be "similar".

I have workaround in the code below but it is quite verbose. Is there a better way?

#include <iostream>
#include <string>

constexpr int get_int(){
    return 47;
}

constexpr std::string_view get_string(){
    return "47";
}

constexpr bool use_str = false;

constexpr auto get_dispatch(){
    if constexpr(use_str){
        return get_string();
    } else{
        return get_int();
    }

}
int main()
{
    // what I want : constexpr auto val =  use_str ? get_string():get_int();
    // what works:
    constexpr auto val = get_dispatch();
    std::cout << val << std::endl;
}
max66
  • 65,235
  • 10
  • 71
  • 111
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • 3
    I don't see something better. I would run with what you have. – bolov Oct 10 '18 at 22:29
  • Tag dispatch might be useful here: `get(int_tag)`, `get(string_tag)` and then `val = get(tag{})`. `std::false_type` and `std::true_type` can be used as tags. – Evg Oct 10 '18 at 22:31
  • @bolov I fear you are right, but maybe somebody has something clever waiting to share with us :) – NoSenseEtAl Oct 10 '18 at 22:36
  • 1
    `std::variant` and `std::visit` are `constexpr`, but not sure it is better... – Jarod42 Oct 10 '18 at 22:40

4 Answers4

4

Another option is to use tag dispatch:

constexpr int get(std::false_type) {
    return 47;
}

constexpr std::string_view get(std::true_type) {
    return "47";
}

int main() {
    constexpr auto val = get(std::bool_constant<use_str>{});
    std::cout << val << std::endl;
}
Evg
  • 25,259
  • 5
  • 41
  • 83
2

Not sure it is better, but with std::variant:

int main()
{
    using my_variant = std::variant<int, std::string_view>;
    constexpr auto val =  use_str ? my_variant{get_string()} : my_variant{get_int()};
    std::visit([](const auto& v) {std::cout << v << std::endl;}, val);
}

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • I tried to oneline it with std::get, but variant is not constexpr friendly, and anyways std::get<!use_str> looks horrible :) – NoSenseEtAl Oct 10 '18 at 23:25
1

this should work:

template <bool>
struct value_chooser;

template<>
struct value_chooser<true>
{
    static constexpr auto value = "47";
};

template<>
struct value_chooser<false>
{
    static constexpr auto value = 47;
};

int main()
{
    auto value1 = value_chooser<true>::value;
    auto value2 = value_chooser<false>::value;
}

live example

Slava
  • 43,454
  • 1
  • 47
  • 90
  • +1. But you can simplify a little using only a specialization; something as `template struct value_chooser { static constexpr auto value = "47"; }; template <> struct value_chooser { static constexpr auto value = 47; };` – max66 Oct 11 '18 at 12:34
  • @max66 yes I know, but I found this way more consistent and explicit - so more readable. – Slava Oct 11 '18 at 13:34
  • @max66 I think your way would be better with enum, when you specify a default case and override some. – Slava Oct 11 '18 at 13:46
  • Also with integer values; is preferable, form the readability point, when there is a significant default behavior and some specializations (IMHO, obviously). – max66 Oct 11 '18 at 16:47
1

I have workaround in the code below but it is quite verbose. Is there a better way?

Working with C++17, you can use if constexpr and it seems to me a good solution.

If you really want something different, I propose a way based on full specialization of template function (a mix between Evg's and Slava's solutions)

I mean something as

#include <iostream>
#include <string>

template <bool>
constexpr auto get_value ()
 { return 47; }

template <>
constexpr auto get_value<true> ()
 { return std::string_view{"47"}; }

int main()
 {
    constexpr auto use_str = false;
    constexpr auto val     = get_value<use_str>();

    std::cout << val << std::endl;
}

As pointed by Slava, a template function with a default version and an explicit specialization for one case, can be less readable.

So, if you prefer a variant a little more verbose but more readable, you can write two explicit specializations for get_value() as follows

template <bool>
constexpr auto get_value ();

template <>
constexpr auto get_value<false> ()
 { return 47; }

template <>
constexpr auto get_value<true> ()
 { return std::string_view{"47"}; }
max66
  • 65,235
  • 10
  • 71
  • 111