类模板名称中的隐藏朋友与内联命名空间中的另一个符号冲突

问题描述

我想为我的类型提供一个隐藏的朋友,并且同时在嵌入式名称空间中还有另一个具有相同名称的对象。一切正常类型都可以正常工作。

但是,如果我有模板,则在实例化模板时编译器会出错

redeFinition of 'cpo' as different kind of symbol

代码如下:

namespace lib{

    struct CPO{
        template<typename... T>
        constexpr decltype(auto) operator()(T&&... args) const{
            cpo(static_cast<T&&>(args)...);
        }
    };

    inline namespace cpo_impl_{
        inline constexpr CPO cpo{};
    }

    struct Foo1{
        friend auto cpo(Foo1){
            return 5;
        }
    };

    template <typename T>
    struct Foo2{
        friend auto cpo(Foo2){
            return 6;
        }
    };
}

int main(){
    lib::Foo1 f1{};
    lib::cpo(f1); // works fine;

    lib::Foo2<int> f2{}; // error here
}

较小的示例在这里

namespace lib{

    inline namespace impl_{
        inline constexpr int foo = 5;
    }

    template <typename T>
    struct Bar{
        friend auto foo(Bar){
            return 4;
        }
    };
}

int main(){
    lib::bar<int> b{};
}

顺便说一句,我知道诸如tag_invoke之类的其他技术,但是在这里我无法控制名称和库

解决方法

您的代码格式正确。这是Clang Bug 37556

使用-std = c ++ 2a编译格式良好的程序:

from keras.utils import to_categorical

def one_hot_encoding(seq):
    mp = dict(zip('ACGT',range(4)))
    seq_2_number = [mp[nucleotide] for nucleotide in seq]
    return to_categorical(seq_2_number,num_classes=4,dtype='int32').flatten()

产生诊断(https://godbolt.org/g/ceWLxY):

namespace X {
    inline namespace Y { int swap; }

    template<class>
    struct S {
        friend void swap(S&,S&) {}
    };
}

int main() {
    X::S<int> s1,s2;
    swap(s1,s2);
}

请注意,如果将S替换为a,则程序将成功编译 非模板类。关于CWG反射镜的讨论证实了这一点 程序格式良好,包括Richard的声明“糟糕,Clang的 模板实例化情况下的重新声明检查不正确 进行重新声明查找,就像在这里查找限定名称一样, 而不是重新声明查找不合格的名称。”

但是没有修复时间表。


作为解决方法,您可以将<source>:6:21: error: redefinition of 'swap' as different kind of symbol friend void swap(S&,S&) {} ^ <source>:11:15: note: in instantiation of template class 'X::S<int>' requested here X::S<int> s1,s2; ^ <source>:2:30: note: previous definition is here inline namespace Y { int swap; } ^ 1 error generated. 包裹在自己的Foo2

inline namespace

这应足以更改声明区域,以避免误诊。同时,名称查找应与命名空间不存在一样进行。