0

I have the following code, which tries to convert a binary number (passed as a list of booleans, least-significant first, variable lenght) into a decimal number:

#include <iostream>
using namespace std;

template<typename T>
int bin_to_dec(int multi, T first) {
    cout<<"mutli"<<multi<<endl;
  return first?multi:0;
}

template<typename T, typename... Args>
int bin_to_dec(int multi, T first, Args... args) {
    cout<<"mutli"<<multi<<endl;
  return (first?multi:0) + adder(multi*2, args...);
}

template<typename T, typename... Args>
int bin_to_dec(T first, Args... args) {
    cout<<"mutli"<<1<<endl;
  return (first?1:0) + adder(2, args...);
}

int main()
{
    cout<<bin_to_dec(true, true, false, true)<<endl;
}

It works quite well, but I would like to make it possible only for booleans, so when I try something like bin_to_dec(1,2,3) it should not compile. I was trying to use something like

template<bool First, bool... Bools>

but I can't figure out how to go further with that. Any ideas?

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
flyjohny
  • 273
  • 1
  • 2
  • 11
  • Take a look at [this question](http://stackoverflow.com/questions/3703658/specifying-one-type-for-all-arguments-passed-to-variadic-function-or-variadic-te) – DarthRubik Jun 04 '16 at 17:51

2 Answers2

2

The obvious approach is to remove the function from the overload set for all template arguments but bool:

template <typename... T>
std::enable_if_t<variadic_and(std::is_same<T, bool>::value...), int>
bin_to_dec(T... bits) {
    // probably delegate to differently named functions as an implementation detail
    // ...
}

variadic_and() would be a constexpr function returning true if all its arguments are true:

constexpr bool variadic_and() { return true; }
template <typename... T>
constexpr bool variadic_and(bool v, T... vs) {
    return v && variadic_and(vs...);
}

With C++17 variadic_and() would be necessary as parameter packs can be expanded with an operator. For example, the implementation of variadic_and() could look like this:

template <typename... T>
constexpr bool variadic_and(T... vs) { return (vs && ...); }

The same approach could be used directly within std::enable_if_t<...>.

Note: the approaches used above requires that the arguments are deduced as bool, i.e., they pretty much need to be of type bool. Since the function shouldn't be callable with int parameters and these would convert to bool, testing whether the argument type is convertable to bool doesn't seem appropriate. However, it may be reasonable to allow some conversions. If so, a corresponding trait would be used in the first paramter to std::enable_if_t.

T.C.
  • 133,968
  • 17
  • 288
  • 421
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 1
    `std::enable_if_t <`[`std::conjunction`](http://en.cppreference.com/w/cpp/types/conjunction)`< std::is_same...>{}, int>`for c++17 – Ryan Haining Jun 04 '16 at 18:47
1

Just use a static assert. This works perfectly well:

int bin_to_dec() {
    return 0;
}

template<typename T, typename ... Args>
int bin_to_dec(T first, Args ... rest)
{
    static_assert(std::is_same<bool, T>::value, "Only valid for bools");
    return (first ? 1 : 0) + (bin_to_dec(rest...) << 1);
}

int main()
{
    cout<<bin_to_dec(true, true, false, true)<<endl;
    cout<<bin_to_dec(1, 2, 3)<<endl;  //compile error
}
Smeeheey
  • 9,906
  • 23
  • 39
  • 2
    I don't really understand the downvote. The above code works exactly as the OP has asked, which is to give the right answer with `bool`s and to fail to compile with anything else. I don't see how the comment above is relevant as the OP never asked for a way to remove a function from the overload set – Smeeheey Jun 04 '16 at 18:52