-1

From this question Why should I avoid std::enable_if in function signatures it seems I should be able to write

#include <type_traits>
#include <iostream>

enum Class {
    Primary,
    Secondary
};

template<Class C>
class Entity {
public:
    template<typename Cls = C, typename Sec = Secondary, std::enable_if<std::is_same<Cls, Sec>::value>::type = 0>
    void onlyLegalForSecondaryEntities() {
        std::cout << "Works" << std::endl;  
    }
};

int main() {
    Entity<Secondary> e;
    e.onlyLegalForSecondaryEntities();
    return 0;
}

But this fails compilation with error prog.cpp:13:7: note: template argument deduction/substitution failed

How do I get this code to compile?

Community
  • 1
  • 1
shane
  • 1,742
  • 2
  • 19
  • 36
  • 6
    `Secondary` is not a type – NathanOliver Nov 08 '16 at 21:02
  • How can I limit the template argument of `Entity` to primary or secondary with a type? – shane Nov 08 '16 at 21:05
  • 2
    @shane, you seem to have a great misunderstanding of basic C++ concepts. I suggest you [choose a book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list), learn from it, and come back to this once you grasped the basics. – StoryTeller - Unslander Monica Nov 08 '16 at 21:09
  • @StoryTeller not helpful. If you can template on an enum, it is a perfectly reasonable misunderstanding to not realize `std::is_same` will not evaluate same-ness between an enum and a type when used as a template argument. – shane Nov 08 '16 at 21:15
  • @shane, it's reasonable to compare types with values!? I stand behind what I said. Get back to the basics. – StoryTeller - Unslander Monica Nov 08 '16 at 21:22

1 Answers1

4

Your use of Class for an enum is a horrible idea. Don't use language keywords with capitalization differences as type names.

C is a compile-time value of type Class. It is not a type.

typename Cls = C attempts to assign a value of type Class to a type. This is an error akin to saying "picking up a sad". sad is not a noun, it is not something you can pick up.

The easiest way to make your code compile is to delete onlyLegalForSecondaryEntities entirely, and all references to it.

In general, under the standard you cannot have a template method which is only valid when certain arguments are passed to the template class it exists within. Doing so makes your program ill formed, no diagnostic required.

This is close:

template<Class Cls = C,
  std::enable_if_t< Cls == Secondary, int> =0
>
void onlyLegalForSecondaryEntities() {
    std::cout << "Works" << std::endl;  
}

except that even on Entity<Primary>, you can do .onlyLegalForSecondaryEntities<Secondary>().

If you don't want to permit this, I'd use CRTP.

template<bool b, class D>
struct empty_if_false {};

template<class D>
struct empty_if_false<true, D> {
  D* self() { return static_cast<D*>(this); }
  D const* self() const { return static_cast<D*>(this); }
  void onlyLegalForSecondaryEntities() {
    // use self() instead of this in this method to get at a this pointer
    std::cout << "Works" << std::endl;  
  }
};

then:

template<Class C>
class Entity:public empty_if_false< C==Secondary, Entity<C> > {

to conditionally have the method.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Can I just point out, that if you add a parameter pack before `Cls`, the user can never explicitly pass something for `Cls`. – StoryTeller - Unslander Monica Nov 08 '16 at 21:28
  • @StoryTeller Maybe. First, you cannot enforce the pack as empty. Second, the *idea* behind the rule is that templates should be able to do early checks that, when the code could never be valid, it is permitted to generate an error, or future standard revisions can add such checks without invalidating old valid programs. Your technique may or may not obey wording; it violates spirit. Of course, template methods of template classes make that rule ambiguous (is it 1 or 2 templates?) – Yakk - Adam Nevraumont Nov 08 '16 at 21:56
  • not sure if it's 1 or 2. The latest standard draft does seem to prohibit it for a primary template, however. So I stand corrected. http://eel.is/c++draft/temp.param#11 Although I doubt it violates spirit, the size of the parameter pack does not affect the validity of the check, – StoryTeller - Unslander Monica Nov 08 '16 at 22:06
  • 1
    No wait, the quote I provided doesn't prohibit it for function templates. In fact it says explicitly **"A template parameter pack of a function template shall not be followed by another template parameter unless [...] or has a default argument"**. So not only am I not violating the standards wording, I'm following it :) – StoryTeller - Unslander Monica Nov 08 '16 at 22:12