无法定义依赖typedef的成员

时间:2015-03-16 11:56:04

标签: c++

我正在编写自定义的懒字符串类。

template <typename charT, typename traits = std::char_traits<charT>>
class lazy_basic_string
{
    class char_proxy
    {
        char_proxy& operator=(charT ch);
    };

    char_proxy operator[](size_type i);
}

然后我想在类声明之外定义这些方法。

template <typename charT, typename traits>
using char_proxy = typename lazy_basic_string<charT, traits>::char_proxy;

template <typename charT, typename traits>
char_proxy<charT, traits>& char_proxy<charT, traits>::operator=(charT ch)
{
    ...
}

但是我收到了编译错误:

  

无法定义依赖typedef char_proxy

的成员

所以我无法弄清楚这里有什么问题。 为什么编译器不能使用快捷方式char_proxy而不是lazy_basic_string :: char_proxy?

2 个答案:

答案 0 :(得分:4)

标准似乎没有特别明确规定。我能得到的最接近的是 [temp.class]

  

3 - 当成员函数,成员类,成员枚举,静态数据成员或成员时   类模板的模板是在类模板定义之外定义的,成员定义是   定义为模板定义,其中 template-parameters 是类模板的定义。该   成员定义中使用的模板参数的名称可能与模板不同   类模板定义中使用的参数名称。类后面的模板参数列表   成员定义中的模板名称应按照与中使用的顺序相同的顺序命名参数   成员的模板参数列表。 [...]

这意味着,虽然没有准确说明,但是一个外行类模板成员定义应该通过名称引用类模板,而不是通过别名模板引用。

应该很容易理解为什么这是必要的;作为别名模板可能导致任意复杂的计算,为了匹配类模板成员与潜在定义的使用,编译器必须对别名模板参数的每个可能组合执行该计算:

template<class T> struct S { void f(); };
template<class T> using s_t = std::conditional_t<sizeof(T) % 8 == 0,
    S<T>, S<T*>>;
template<class T> void s_t<T>::f() {}

int main() { S<int> s; s.f(); }    // defined?

有趣的是,clang(3.7)允许在类模板成员定义中使用别名模板,但仅限于直接身份计算:

template<class> struct T { void f(); };
template<class C> using id_t = C;
template<class C> using t_t = T<id_t<C>>;
template<class C> void t_t<C>::f() {}    // OK??

答案 1 :(得分:1)

您使用的是什么编译器?使用GCC 4.8,我发现无法编译,但在Visual Studio 2015 Preview中,如果稍微修改代码,它会成功编译:

template <typename charT, typename traits>
char_proxy<charT, traits>& lazy_basic_string<charT, traits>::char_proxy::operator=(charT ch)
{
    return {};
}

或者,如果您愿意:

template <typename charT, typename traits>
typename lazy_basic_string<charT, traits>::char_proxy& char_proxy<charT, traits>::operator=(charT ch)
{
    return{};
}

您将注意到,我只能将别名用作返回类型或访问运营商名称,但不能同时使用两者。

我认为您在标准中发现了某种暗区,因为以下代码在VS和GCC上也有不同的行为。它在VS 2015上编译,但在GCC中没有编译:

template<typename T>
class A {
    class B {
        B& test();
    };
};

template<typename T>
using B_alias = typename A<T>::B;

template<typename T>
B_alias<T>& B_alias<T>::test()
{
    return{};
}

在这种情况下,在VS上我可以使用别名来访问函数名称并指定返回类型。