模板扣除上下文中的受保护成员:编译错误,替换失败或成功?

时间:2015-08-11 22:05:55

标签: c++11 g++ clang access-modifiers sfinae

g ++ 5.2.1在模板演绎上下文中遇到私有方法地址时无法编译,而clang 3.5只丢弃专业化。
当clang 3.5有时不能这样做时,g ++ 5.2.1可以在类模板参数列表中访问父母/朋友的受保护成员 在哪些情况下哪个错了?

更确切地说:

  • 在模板扣减上下文中尝试访问不可访问的受保护/私有成员时,编译器是否会导致硬错误?我在第一个例子中做错了吗?

  • 如果没有,编译器在尝试访问(在模板扣除上下文中)受保护的成员时,是否应该放弃专门化:

    • 此特定专业的基类

    • 将此特定实例化声明为朋友的类

    • 一个朋友类,它将此模板的任何实例化声明为朋友

第一个问题似乎已经回答了here(答案似乎是"不,代码不是格式错误,编译器应该简单地放弃这个专业化&#34 )。但是,由于它是第二个问题的先决条件(并且g ++ 5.2.1似乎不同意),我想绝对肯定它的g ++ 5.2.1哪个是错的,不是我

带有示例

的更长版本

我想让特征能够检测某些函数/方法是否已实现,即使它们是某些类的受保护成员(如果你发现这很奇怪,我将解释为什么我想在这个问题的结尾,以便你可以告诉我,我完全错了,应该学习如何设计我的课程,并建议我采用更清洁的方法)。
我的问题是我的每一次尝试都失败了clang或g ++(奇怪的是,不是同时两者):有时它会编译但不能提供预期的结果,有时它根本就没有编译。
即使看起来它不实用,我至少想知道编译器何时出错,以及何时编写格式错误的代码。因此这个问题。

我使用C ++ 11方言,而我的clang版本实际上是XCode 5提供的编译器,即Apple LLVM 6.0版(clang-600.0.57)(基于LLVM 3.5svn)。

为了更好地说明我的问题,这里是一个最小的例子,其中clang编译但g ++ 5.2.1没有:

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

struct PrivateFoo {
private:
  void foo() {}
};

/* utilities */

// reimplement C++17's std::void_t
template<class...>
struct void_type {
  typedef void type;
};

template<class... T>
using void_t = typename void_type<T...>::type;

// dummy class used to check whether a pointer to (possibly member) function
// exists with the good signature
template<class T, T>
struct check_signature {};

/* traits */

template<class C, class = void>
struct has_foo : std::false_type {};

template<class C>
struct has_foo<C, void_t<check_signature<void(C::*)(), &C::foo>>> :
std::true_type {};

int main() {
  std::cout << std::boolalpha;
  std::cout << "PrivateFoo has foo: " << has_foo<PrivateFoo>::value << '\n';
  return 0;
}
使用clang 3.5输出

PrivateFoo has foo: false

g ++ 5.2.1错误:

access_traits.cpp : [in instantiation of] ‘struct has_foo<PrivateFoo>’ :
access_traits.cpp:37:61:   required from here
access_traits.cpp:7:8: [error]: ‘void PrivateFoo::foo()’ is private
   void foo() {}
        ^
access_traits.cpp:32:56: [error]: [in context]
 struct has_foo<C, void_t<check_signature<void(C::*)(), &C::foo>>> :
                                                        ^
access_traits.cpp:7:8: [error]: ‘void PrivateFoo::foo()’ is private
   void foo() {}
        ^
access_traits.cpp:32:56: [error]: [in context]
 struct has_foo<C, void_t<check_signature<void(C::*)(), &C::foo>>> :
                                                        ^
access_traits.cpp: [in function] ‘int main()’:
access_traits.cpp:7:8: erreur: ‘void PrivateFoo::foo()’ is private
   void foo() {}
        ^
access_traits.cpp:37:42: [error]: [in context]
   std::cout << "PrivateFoo has foo: " << has_foo<PrivateFoo>::value << '\n';

(括号中的文字已翻译,最初是我的母语)

以下是编译但不同意foo是否可访问的示例:

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

struct ProtectedFoo {
protected:
  void foo() {}
};

/* utilities */

// reimplement C++17's std::void_t
template<class...>
struct void_type {
  typedef void type;
};

template<class... T>
using void_t = typename void_type<T...>::type;

// dummy class used to check whether a pointer to (possibly member) function
// exists with the good signature
template<class T, T>
struct check_signature {};

/* traits */

namespace detail {
  template<class C, class = void>
  struct has_foo_helper : std::false_type {};

  template<class C>
  struct has_foo_helper<C, void_t<decltype(std::declval<C>().foo())>> :
  std::true_type {};
}

template<class C>
struct has_public_or_protected_foo : protected C {
  template<class, class>
  friend class detail::has_foo_helper;

  static constexpr bool value =
  detail::has_foo_helper<has_public_or_protected_foo<C>>::value;
};

int main() {
  std::cout << std::boolalpha;
  std::cout << "ProtectedFoo has foo: ";
  std::cout << has_public_or_protected_foo<ProtectedFoo>::value << '\n';
  return 0;
}
使用clang 3.5输出

ProtectedFoo has foo: false

使用g ++ 5.2.1输出:

ProtectedFoo has foo: true

最后这里是一个编译并同意他们应该能够访问foo的例子:

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

struct ProtectedFoo {
protected:
  void foo() {}
};

/* utilities */

// reimplement C++17's std::void_t
template<class...>
struct void_type {
  typedef void type;
};

template<class... T>
using void_t = typename void_type<T...>::type;

// dummy class used to check whether a pointer to (possibly member) function
// exists with the good signature
template<class T, T>
struct check_signature {};

/* traits */

namespace detail {
  template<class C, class D, class = void>
  struct has_foo_helper : std::false_type {};

  template<class C, class D>
  struct has_foo_helper<C, D, void_t<check_signature<void(C::*)(), &D::foo>>> :
  std::true_type {};
}

template<class C>
struct has_public_or_protected_foo : protected C {
  template<class, class, class>
  friend class detail::has_foo_helper;

  static constexpr bool value =
  detail::has_foo_helper<C, has_public_or_protected_foo<C>>::value;
};

int main() {
  std::cout << std::boolalpha;
  std::cout << "ProtectedFoo has foo: ";
  std::cout << has_public_or_protected_foo<ProtectedFoo>::value << '\n';
  return 0;
}
使用clang 3.5输出

ProtectedFoo has foo: true

使用g ++ 5.2.1输出:

ProtectedFoo has foo: true

这里还有一个更完整的例子,总结了所有这些:

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

/* test classes */

struct PublicFoo {
  void foo() {}
};

struct ProtectedFoo {
protected:
  void foo() {}
};

struct PrivateFoo {
private:
  void foo() {}
};

struct NoFoo {};

/* utilities */

// reimplement C++17's std::void_t
template<class...>
struct void_type {
  typedef void type;
};

template<class... T>
using void_t = typename void_type<T...>::type;

// dummy class used to check whether a pointer to (possibly member) function
// exists with the good signature
template<class T, T>
struct check_signature {};

/* traits */

namespace detail {
  template<class C, class D, class = void>
  struct has_foo_helper : std::false_type {};

  template<class C, class D>
  struct has_foo_helper<C, D, void_t<check_signature<void(C::*)(), &D::foo>>> :
  std::true_type {};

  template<class C, class = void>
  struct may_call_foo_helper : std::false_type {};

  template<class C>
  struct may_call_foo_helper<C, void_t<decltype(std::declval<C>().foo())>> :
  std::true_type {};
}

template<class C>
struct has_foo : detail::has_foo_helper<C, C> {};

template<class C>
struct may_call_foo : detail::may_call_foo_helper<C> {};

template<class C>
struct has_protected_foo : protected C {
  template<class, class, class>
  friend class detail::has_foo_helper;

  static constexpr bool value =
  detail::has_foo_helper<C, has_protected_foo<C>>::value;
};

template<class C>
struct may_call_protected_foo : protected C {
  template<class, class>
  friend class detail::may_call_foo_helper;

  static constexpr bool value =
  detail::may_call_foo_helper<may_call_protected_foo<C>>::value;
};

/* test */

template<class T>
void print_info(const char* classname) {
  std::cout << classname << "...\n";
  // comment this if you want to compile with g++
  //*
  std::cout << "has a public method \"void foo()\": ";
  std::cout << has_foo<T>::value << '\n';
  std::cout << "has a public or protected method \"void foo()\": ";
  std::cout << has_protected_foo<T>::value << '\n';
  //*/
  std::cout << "has a public method \"foo\" callable without any arguments: ";
  std::cout << may_call_foo<T>::value << '\n';
  std::cout << "has a public or protected method \"foo\" callable without any "
               "arguments: ";
  std::cout << may_call_protected_foo<T>::value << '\n';
  std::cout << '\n';
}

int main() {
  std::cout << std::boolalpha;
  // both g++ 5.2.1 and clang 3.5 compile
  print_info<PublicFoo>("PublicFoo");
  // g++ 5.2.1 fails to compile has_foo, clang 3.5 compiles fine
  print_info<ProtectedFoo>("ProtectedFoo");
  // g++ 5.2.1 fails to compile, clang 3.5 compiles fine
  print_info<PrivateFoo>("PrivateFoo");
  // both g++ 5.2.1 and clang 3.5 compile
  print_info<NoFoo>("NoFoo");
  return 0;
}

为什么我要这样做?

如果您不想了解详情,请跳过此处。我刚刚写了这篇文章,以防你对我试图检测受保护成员的想法感到震惊和/或对我问这个问题的原因感到好奇。

我正在编写一些由其他迭代器构建的迭代器模板类,我厌倦了编写多个特化,这取决于这些迭代器是否满足某些要求(ForwardIterator,BidirectionalIterator,RandomAccessIterator ......虽然我的迭代器实际上遇到了一些宽松的版本在这些概念中,它们是一种代理迭代器&#34;但它在这里并不真正相关)。 例如,如果我只使用随机访问迭代器,我的新自定义迭代器可以(也应该)也是某种随机访问迭代器,因此实现operator+=operator+operator-=,{ {1}},operator-operator<operator>operator<=。但是,其中一些运算符可以很容易地从其他运算符中推导出来,只有在我使用的所有迭代器都是随机访问迭代器时才应该定义它们。

我终于认为我只是提供一些东西来提供默认实现(如果可用)。但是,我并不是非常喜欢operator>=方式,因为迭代器不会非常方便和可读(另外,我也不能将它们与一些标准实用程序一起使用) )。
我最终选择的设计包括一个模板类,它将从最小的实现中构建一个完整的迭代器(例如只包含std::allocator_traitsoperator+=operator==和{{ 1}}定义)通过拥有&#34; mixins&#34;的继承链。 (它的祖先是我的最小迭代器)检测它们所需的函数/方法是否在它们的基类中可用并定义它们的默认方法。
尽管如此,还是有一种劣势。有时我想返回对最终迭代器的引用(例如在operator*中)。如果它是我重新定义的一种方法,我可以通过CRTP和operator>轻松解决这个问题,但如果我的mixins根本不接触该方法怎么办? 我想我应该禁止使用我没有触及的任何方法并使用受保护的访问说明符继承我的最小迭代器...但是现在我的特性无法检测到某些受保护方法的可用性。
因此,我希望能够检测某些成员是否具有公开或受保护的可见性。

0 个答案:

没有答案