有没有办法使用模板来避免缩小转换范围

Is there a way using templates to avoid narrowing conversion?

本文关键字:缩小 转换 范围 有没有      更新时间:2023-10-16

以下列例子为例:

#include <iostream>
#include <vector>
#include <type_traits>
template<typename T>struct X
{
    std::vector<T> v;
    void clear() { v.clear(); }
    ~X(){}
    X(){ clear(); }
    X(const X &x) { v=x.v; }
    //template<typename ...I> X(I...i) : v({static_cast<T>(i)...}) {} //<---gets rid of warnings only, still narrowing
    template<typename ...I> X(I...i) : v({i...}) {}
    template<typename U> X(const U &u) { for (auto e:u.v) v.push_back((T)e); }
    bool operator==(const X &x) const { return (v==x.v); }
    template<typename U> bool operator==(const U &u) { X<T> x(u); return (v==x.v); }
    void show(const std::string &s) const
    {
        using TYPE=typename std::common_type<T,int>::type;
        std::cout << s << " = ";
        for (auto x:v) std::cout << (TYPE)x << " ";
        std::cout << "n";
    }
};
template<typename T> bool testit(const T a, const T b)
{
    return (a==b);
}
template<typename T> bool testit(const X<T> a, const X<T> b)
{
    return (a==b);
}
template<typename T, typename U> bool testit(const X<T> a, const X<U> b)
{
    X<T> c(b); // this is the problem - t=int, U=double => narrowing
    return testit(a, c);
/*
    // tried doesn't work
    if (sizeof(decltype(T()))<sizeof(decltype(U())))
    {
        X<U> c(a);
        X<U> d(b);
        return testit(c,d);
    }
    else
    {
        X<T> c(a);
        X<T> d(b);
        return testit(c,d);
    }
*/
/*
    //doing the following causes a segfault -- I have no idea why
    using T1=typename std::common_type<T, int>::type;
    using T2=typename std::common_type<U, int>::type;
    using TT=typename std::common_type<T1, T2>::type;
    X<TT> c(a);
    X<TT> d(b);
    return testit(d,b);
*/
}

int main(int argc, const char *argv[])
{
    auto res=[](bool b)->std::string{ return b?"OK":"Fail"; };
    auto test=[res](const std::string s, bool btest, bool bexp){ std::cout << s << " - " << res(btest==bexp) << "n"; };
    test("(1,1)", testit(1,1), 1);
    test("(1,2)", testit(1,2), 0);
    test("(abc,abc)", testit("abc","abc"), 1);
    test("(abc,aaa)", testit("abc","aaa"), 0);
    X<int> xa{1,2,3};               xa.show("xa");
    X<double> xb{1.0, 2.0, 3.0};    xb.show("xb");
    X<char> xc{2,4,6};              xc.show("xc");
    X<double> xd{1.1, 2.1, 3.1};    xd.show("xd");

    test("(xa, xb)", testit(xa,xb), 1);
    test("(xa, xc)", testit(xa,xc), 0);
    test("(xb, xc)", testit(xb,xc), 0);
    test("(xc, {2.0, 4.0, 6.0})", testit(xc, {2.0, 4.0, 6.0}), 1);
    test("(xc, {2.0, 4.1, 6.0})", testit(xc, {2.0, 4.1, 6.0}), 0);
    test("(X<int>({4, 5, 6}), {4.0, 5.0, 6.0})", testit(X<int>({4, 5, 6}), {4.0, 5.0, 6.0}), 1);
    test("(X<int>({4, 5, 6}), {4.0, 5.0, 6.1})", testit(X<int>({4, 5, 6}), {4.0, 5.0, 6.1}), 0);
    return 0;
}

当 T 为 int 且 U 为双精度时,这会导致变窄。

由于我不知道模板参数在运行时的顺序,有没有办法避免这种缩小 - 以便将 int 转换为双精度 - 无论参数的顺序如何?

警告来自具有可变参数包的 X 的一个构造函数。您可以通过显式强制转换来使编译器静音:

template<typename ...I> X(I...i) : v({static_cast<T>(i)...}) {}

这摆脱了警告,比如说,X<char> xc{2,4,6};,我认为你不介意缩小。

实际上避免在

testit内部构造X时缩小,可以使用std::common_type

X< std::common_type_t<T,U> > c(b);

若要使警告静音,请显式执行强制转换:

template<typename ...I> X(I...i) : v({static_cast<T>(i)...}) {}

演示