在clang中返回模板类内朋友函数的类型推导

时间:2018-02-02 05:11:24

标签: templates g++ c++14 friend clang++

所以,我正在尝试做的事情并不是针对课堂朋友定义的真正意图用例。但是它确实可以在g ++下工作,而且我能说的最好是按照C ++ 14规范工作。

为了讨论的目的,clang是5.0.0而gcc是7.2.0,虽然我已经测试了其他最近和HEAD版本并得到了相同的结果。用c ++ 14标记编译的所有内容。

我感兴趣的案件的最小复制品如下。

#include <iostream>

auto foo();

template <typename T>
class Foo
{
    friend auto foo()
    {
        return (T)3.14;
    }
};

template <typename T>
void bar(T){}

int main()
{
    bar(Foo<float>{});
    std::cout << foo() << std::endl;
    return 0;
}

Gcc行为:编译并运行。产出:3.14

Clang行为:拒绝编译。错误:'foo'

的类型冲突

这本身就是完全错误的,但是当我取消Foo的模板化时,auto不会给clang任何问题,我决定尝试给foo函数一个虚拟模板。

#include <iostream>

template <typename X = int>
auto foo();

template <typename T>
class Foo
{
    template <typename X>
    friend auto foo()
    {
        return (T)3.14;
    }
};

template <typename T>
void bar(T){}

int main()
{
    bar(Foo<float>{});
    std::cout << foo() << std::endl;
    return 0;
}

Gcc行为:编译并运行。产出:3.14

Clang行为:编译并运行。输出:3

是的,当使用clang编译时,foo的类型推导完全取决于声明中的默认模板参数,尽管没有出现在定义中的任何位置。 O_O

最后,我决定尝试通过引入using指令来修复这种特殊行为。

#include <iostream>

template <typename X = int>
auto foo();

template <typename T>
class Foo
{
    using Type = T;
    template <typename X>
    friend auto foo()
    {
        return (Type)3.14;
    }
};

template <typename T>
void bar(T){}

int main()
{
    bar(Foo<float>{});
    std::cout << foo() << std::endl;
    return 0;
}

Gcc行为:编译并运行。产出:3.14

Clang行为:编译器崩溃。我试过的每一个版本。

崩溃是一个明显的错误,但我对前两个例子更感兴趣。这也是铿锵的错误吗?或者是否存在一些微妙的原因导致为什么这应该是未定义的行为,而我只是对gcc感到幸运?更重要的是,任何人都可以想到一个适用于gcc和clang的解决方法吗?我正在尝试做的是检测Foo实例化的类型,前提是它在Foo之外的编译时是唯一的。

1 个答案:

答案 0 :(得分:0)

虽然我还不完全确定为什么Clang会这样做,但我找到了一个可以在Clang和GCC上编译的解决方法。后者引发了一个警告,但它可以被抑制,或者可以使用ifdef引入轻微的定义变化,以使代码在两者上都干净地编译。

#include <iostream>

template <typename T = void>
class A
{
public:
    friend auto foo(A<T>);
};

template <typename T>
class Foo
{
    friend auto foo(A<void>)
    {
        return (T)3.14;
    }
};

template <typename T>
void bar(T){}

int main()
{
    bar(Foo<float>{});
    std::cout << foo(A{}) << std::endl;
    return 0;
}

这允许扣除foo()的返回类型,使其基于Foo实例化的模板参数,即使它们发生在不同的代码块中,这正是我所寻找的。