0
  template<typename FilterComponent, typename ...FilterComponents>
  std::bitset<components_count> component_impl(std::bitset<components_count> &b){
    b.set(FilterComponent::get_id());
    return component_impl<FilterComponents...>(b); //ambiguous
  }
  template<typename FilterComponent>
  std::bitset<components_count> component_impl(std::bitset<components_count> &b){
    b.set(FilterComponent::get_id());
    return b;
  }
  template<typename ...FilterComponents>
  std::bitset<components_count> component_mask(){
    std::bitset<components_count> b;
    component_impl<FilterComponents...>(b);
    return b;
  }

Why is this function call ambiguous? I wanted to call it like component_mask<Foo,Bar,Baz>();

error: call to member function 'component_impl' is ambiguous
    return component_impl<FilterComponents...>(b);
Maik Klein
  • 15,548
  • 27
  • 101
  • 197
  • Testcase please...... Why do I have to ask for a testcase on every question? Why is it so hard to comprehend its importance? And why is nobody constructing them _before_ posting on the internet?! Auch!!! – Lightness Races in Orbit Dec 29 '14 at 00:33
  • @LightnessRacesinOrbit Okay give me one moment. – Maik Klein Dec 29 '14 at 00:34
  • 7
    When `FilterComponents` has only one element, both function templates can be instantiated with the exact same template arguments. – David G Dec 29 '14 at 00:34
  • Usually partial ordering will save you, but not in this case since your template parameters are not in the function parameters' types. – T.C. Dec 29 '14 at 00:45
  • http://coliru.stacked-crooked.com/a/a6469a80870492cf – Lightness Races in Orbit Dec 29 '14 at 00:47
  • 1
    @LightnessRacesinOrbit: That's why questions should include the **complete** error message. Of course, looking at the complete message, the question probably wouldn't have been needed. IDEs do a tremendous disservice by offering an "error list" window that truncates the error information at the first linebreak. – Ben Voigt Dec 29 '14 at 01:01
  • Also dupe of http://stackoverflow.com/q/14005020/103167 – Ben Voigt Dec 29 '14 at 01:06

2 Answers2

5

When FilterComponents... is empty or has only one element, there is an ambiguity because both function templates are equally viable. You can add a second template argument to the first template declaration to resolve the ambiguity (like R Sahu does). You can also unpack the arguments into an initializer-list to get the same effect:

template<typename... FilterComponents>
std::bitset<components_count> component_impl(std::bitset<components_count>& b)
{
    using discard = int[];
    (void)discard{ 0, (b.set(FilterComponents::get()), void(), 0)... };
    return b;
}

The first 0 and trailing 0 is to compensate for an empty parameter pack and to fill the the list with integers thus discarding the void() type. The void() "erases" the return value of set() thereby preventing the possible use of an overload operator,() from its return type (for which we know std::bitset::set() doesn't have but when you're dealing with a general case it's helpful to use it).

This also removes the need for a second overload to help with recursion.

Community
  • 1
  • 1
David G
  • 94,763
  • 41
  • 167
  • 253
  • 2
    I guess it removes the need for a separate helper function at all. – Ben Voigt Dec 29 '14 at 00:59
  • Good solution. An explanation of those `0` and `void()` wouldn't go amiss! – Lightness Races in Orbit Dec 29 '14 at 01:04
  • 1
    The first 0 should go outside the pack expansion. – T.C. Dec 29 '14 at 01:08
  • 1
    The `void()` you took out indeed isn't necessary here, but it was a good general tip that I thought was nice of you to include: it made sure the built-in `,` was used regardless of how `b.set` was declared, as no custom `operator,` can take a parameter of type `void`. It's not necessary here since `b.set`'s return type is known not to have any overloaded `operator,`, but it is useful if this answer is extended to call other methods returning other types. –  Dec 29 '14 at 01:15
  • 1
    @hvd Thanks, I will add it back in. – David G Dec 29 '14 at 01:19
2

When component_impl is called with one typename, it is ambiguous. The first version matches with an empty typename pack. The second version also is a match.

Change the variadic template version to have two typenames before the typename pack. That will disambiguate the two functions.

template<typename FilterComponent1, typename FilterComponent2, typename ...FilterComponents>
std::bitset<components_count> component_impl(std::bitset<components_count> &b){
   b.set(FilterComponent1::get_id());
   return component_impl<FilterComponent2, FilterComponents...>(b);
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270