javaLover
javaLover

Reputation: 6425

set public/private on template function for some certain template parameter

Is it possible to make a certain template function have 2 accessibility level for some certain template parameter? (via splitting into 2 functions?)

class B{
    enum ENU{
        T0,T1,T2   
    }
    template<ENU T=T0> someType f(){  ... } //want T1,T2 = public,  T0 = private
};

Current usage (the solution should not change it):-

B b;
int aa=b.f<T0>(); //should fail
std::string bb=b.f<T1>();// should ok

Edit: B has a lot of functions like this.



Here is the full code (just in case someone want to edit or use) https://ideone.com/ryNCml.

Upvotes: 3

Views: 161

Answers (3)

Given you want to support only a finite set of template arguments, I would write three functions which are not template functions, and give them the right visibility. Then make them delegate to a private template function which does the work. This would look like:

class B{
public:
    enum ENU{
        T0,T1,T2   
    }
private:
    template<ENU T=T0> int f(){ 
          std::cout<<"In enum "<<T<<std::endl;
          return 0;
    }

protected:
    someType fT0() { return f<T0>(); }
public:
    someType fT1() { return f<T1>(); }
    someType fT2() { return f<T2>(); }

};

Contrary to your requirements, the usage changes - but this is often the simplest approach:

B b;
int aa=b.fT0(); // fails
int bb=b.fT1();// ok

Alternatively, you can make the template be public, but give it a dummy argument (with a default), and make the type of the dummy argument depend on the template parameter (via traits). If the type of the dummy is a private class, the template will only be callable by a member.

template <ENU T>
struct protection_traits;

class B{
friend class protection_traits<T0>; // So it has access to Protected.
protected:
    struct Protected{};
public:
    struct Public{};

    enum ENU{
        T0,T1,T2   
    }
    template<ENU T=T0> int f( typename protection_traits<T>::type = {})
      { std::cout<<"In enum "<<T<<std::endl; }
};

template <ENU T>
struct protection_traits 
{
    typedef B::Public type;   // Default to public
};
template<>
struct protection_traits<T0> 
{ 
    typedef B::Protected type;  // But T0 uses Protected
};

Usage:

B b;
int aa=b.f<T0>(); // fails (no access to B::Protected)
int bb=b.f<T1>(); // ok

Note: This latter solution hasn't been fed to a compiler. There will be typos.

Upvotes: 0

skypjack
skypjack

Reputation: 50550

As far as I can understand, you want to forbid the use of T0 as a template parameter while invoking member method f.
To do that, you can use either std::enable_if or a static_assert.
It follows a minimal, working example:

#include<type_traits>

class B {
public:
    enum ENU { T0,T1,T2 };

    template<ENU T>
    std::enable_if_t<(T==T1||T==T2),int>
    f() { return 42; }

    template<ENU T>
    int g(){
        static_assert(T==T1||T==T2, "not allowed");
        return 42;
    }
};

int main() {
    B b;
    b.f<B::T1>();
    // It doesn't work
    //b.f<B::T0>();
    b.g<B::T1>();
    // It doesn't work
    //b.g<B::T0>();
}

Upvotes: 2

JVApen
JVApen

Reputation: 11317

I doubt what you are trying to do is possible, as specializing functions on a value ain't allowed in C++.

Though if you don't need enumerations, you could write something similar:

class B {
public:
     struct T0{};
     struct T1{};
     struct T2{};
     template<typename T> void f(T, ...) {
          static_assert(std::is_same_v<T, T1> || std::is_same_v<T, T2>); 
     }
private:
     void f(T0, ...) {}
};

int main(int argc, char **argv) {
    B b{};
    b.f(T1{}); // Should compile
    b.f(T0{}); // Should not compile
}

If you would use the same function implementation, you can either forward this to a common method, or simply put T0 private.

Alternatively, you could make use of a proxy object which could convert the value, though I'm not sure if this is standard C++ of an extension of the compiler I'm familiar with:

class B {
public:
    enum class T { //< Strong typed!
        T0,
        T1,
        T2
    }
    template <T t>
    struct TWrapper {};

    template <T ActualT>
    void f(..., TWrapper<ActualT> tw = TWrapper<ActualT>{}); 
private:
    template <>
    struct TWrapper<T0> {};
}

Upvotes: 2

Related Questions