成员函数模板专业化可能具有与主模板不同的访问级别吗?

时间:2014-01-09 23:44:18

标签: c++ templates c++11

a question I had about deleting functions 的回答提到了成员函数模板如何不能在类范围内专门化。这让我想知道成员函数模板专业化是否可能具有与主模板不同的访问级别。在下面的代码中,我正在尝试对公共成员函数模板进行私有化:

#include <iostream>

class Foo {
public:
  template<typename T>
  void func(T) { std::cout << "Public\n"; }

private:
  template<>
  void func<char>(char) { std::cout << "Private\n"; }

  friend int main();
};

int main()
{
  Foo f;
  f.func(10);
  f.func('a');
}

使用最新的MSVC,这会编译,运行并产生预期的输出:

Public
Private

使用g ++ 4.8和Clang 3.2,代码被拒绝。 Clang说:

error: explicit specialization of 'func' in class scope
void func<char>(char) { std::cout << "Private\n"; }
     ^

据推测,g ++和Clang正在使用14.7.3 / 2的C ++ 11作为他们行为的基础,但我认为可能会有一些摆动空间,因为3.3.6 / 3表示全球范围是命名空间和全局命名空间(间接)包含模板特化。

我的问题不是关于标准的这些部分或任何这些编译器的行为,而是关于成员函数模板是否有可能具有与通用模板不同的访问级别的特化。例如,是否可以拥有公共成员函数模板和该模板的私有特化?

1 个答案:

答案 0 :(得分:2)

我们总是可以手动完成。

一些随机的SFINAE机器:

#include <iostream>
#include <utility>
#include <type_traits>

template<typename T> constexpr bool IsInt() { return std::is_same<T,int>::value; }
template<std::size_t>
struct SecretEnum {
  enum class hidden {};
};
template<bool b, int i=1> using EnableIf = typename std::enable_if<b,typename SecretEnum<i>::hidden>::type;

class Foo {
public:
  template<typename T, EnableIf< !IsInt<T>(), 1 >...>
  void func(T) { std::cout << "Public\n"; }

private:
  template<typename T, EnableIf< IsInt<T>(), 2 >...>
  void func(T) { std::cout << "Private with int\n"; }

  friend int main();
};

int main()
{
  Foo f;
  f.func(10);
  f.func('a');
}

现在这个技巧对clang不起作用,因为我做了SFINAE和方法区分我最后检查。但是这可以用其他类似的技巧替换(比如第二个参数中基于指针的默认参数 - 用EnableIf< IsInt<T>(), 2 >...替换EnableIf< IsInt<T>(), 2 >* = nullptr或者用clang替换一些。我只是觉得它不那么有吸引力。)

那么上面发生了什么? func有两个不同的重载。两者都是模板函数,其中一个参数是T,还有一个秘密枚举的包,当且仅当TIsInt<T>()!IsInt<T>()测试匹配时,其类型才有效分别。包的类型在两种情况下不同(一种是SecretEnum<2>::hidden,另一种是SecretEnum<1>::hidden),因此它们的签名足以满足大多数C ++ 11编译器(clang认为它们是最后我检查,产生错误,我相信铿锵是错误的。)

当您调用func<blah>时,它会检查两个func中哪个(如果有)是合适的。由于他们的条件彼此完全相反,因此只有其中一个是正确的。

实际上,我们正在进行手动专业化。

在C ++ 1y中,如果星星正确对齐,我们可以template<IsInt T>template<IsNotInt T>,并且进入技术报告的概念lite可以让它工作。