简体   繁体   English

如何生成代码以使用自定义零值初始化std :: vector(如果它以T :: Zero形式存在)?

[英]how to generate code to initialize a std::vector with a custom Zero value if it exists as T::Zero?

BACKGROUND 背景

I have a container class that has a std::vector<T> member that I initialize with the constructor that takes size_t n_items . 我有一个容器类,该容器类具有一个std::vector<T>成员,该成员使用带有size_t n_items的构造函数进行初始化。 I would like to initialize that vector with my own Zero() function which returns 0; 我想用我自己的Zero()函数初始化该向量,该函数returns 0; by default, but if static member T::Zero exists I want to return that instead. 默认情况下,但如果存在静态成员T::Zero ,我想返回它。

In my first attempt, I use expression SFINAE, but that failed due to ambiguous overload since both the generic version of Zero and the Zero have the same signature with no arguments. 在我的第一次尝试中,我使用表达式SFINAE,但由于模糊的重载而失败,因为零和零的通用版本具有相同的签名而没有参数。 So now, I'm trying to convert the code into classes with operator() . 所以现在,我正在尝试使用operator()将代码转换为类。

I think I need to use std::enable_if somehow, but I'm not sure how to go about coding this. 我想我需要以某种方式使用std::enable_if ,但我不知道如何编写这个。

FAILED ATTEMPT 失败的尝试

#include <cassert>
#include <iostream>
#include <vector>

template<typename T>
struct Zero
{
        T operator() const { return 0; }
};

template<typename T>
struct Zero
{
        auto operator() const ->
                decltype( T::Zero )
        {
                return T::Zero;
        }
};

struct Foo
{
        char m_c;
        static Foo Zero;

        Foo() : m_c( 'a' ) { }
        Foo( char c ) : m_c( c ) { }
        bool operator==( Foo const& rhs ) const { return m_c==rhs.m_c; }
        friend std::ostream& operator<<( std::ostream& os, Foo const& rhs )
        {
                os << (char)(rhs.m_c);
                return os;
        }
};

Foo Foo::Zero( 'z' );

int
main( int argc, char** argv )
{
        std::vector<unsigned>  v( 5, Zero<unsigned>() );
        std::vector<Foo>       w( 3, Zero<Foo>()      );

        for( auto& x : v )
                std::cout << x << "\n";
        std::cout << "---------------------------------\n";
        for( auto& x : w )
        {
                assert( x==Foo::Zero );
                std::cout << x << "\n";
        }

        std::cout << "ZERO = " << Foo::Zero << "\n";

        return 0;
}
#include <string>
#include <iostream>
#include <vector>
#include <type_traits>

namespace detail
{
    template<class T, class = decltype(T::zero)>
    std::true_type has_zero_impl(int);

    template<class T>
    std::false_type has_zero_impl(short);
}

template<class T>
using has_zero = decltype(detail::has_zero_impl<T>(0));

template<class T, class = has_zero<T>>
struct zero
{
    constexpr static T get() { return T(); }
};

template<class T>
struct zero<T, std::true_type>
{
    constexpr static auto get() -> decltype(T::zero)
    {  return T::zero;  }
};

Usage example: 用法示例:

template<>
struct zero<std::string>
{
    static constexpr const char* get()
    {  return "[Empty]";  }
};

struct foo
{
    int m;
    static constexpr int zero = 42;
};

int main()
{
    std::cout << zero<int>::get() << "\n";
    std::cout << zero<std::string>::get() << "\n";
    std::cout << zero<foo>::get() << "\n";
}

Compact version w/o trait: 没有特征的精简版:

template<class T>
struct zero
{
private:
    template<class X>
    constexpr static decltype(X::zero) zero_impl(int)
    { return X::zero; }

    template<class X>
    constexpr static X zero_impl(short)
    { return X(); }

public:
    constexpr static auto get()
    -> decltype(zero_impl<T>(0))
    {
        return zero_impl<T>(0);
    }
};

template<>
struct zero<std::string>
{
    constexpr static const char* get()
    {  return "[Empty]";  }
};

Maybe: 也许:

#include <iostream>
#include <vector>

template <typename T>
inline T zero() {
    return T();
}

template <>
inline std::string zero<std::string>() {
    return "[Empty]";
}

int main() {
    std::vector<std::string> v(1, zero<std::string>());
    std::cout << v[0] << '\n';
}

I have a half good news: I found a very simple way (leveraging SFINAE), however the output is not exactly what I expected :) 我有一个好消息:我发现了一种非常简单的方法(利用SFINAE),但是输出结果与我期望的不完全相同:)

#include <iostream>
#include <string>
#include <vector>

// Meat
template <typename T, typename = decltype(T::Zero)>
auto zero_init_impl(int) -> T { return T::Zero; }

template <typename T>
auto zero_init_impl(...) -> T { return T{}; }

template <typename T>
auto zero_init() -> T { return zero_init_impl<T>(0); }

// Example
struct Special { static Special const Zero; std::string data; };

Special const Special::Zero = { "Empty" };

int main() {
    std::vector<int> const v{3, zero_init<int>()};
    std::vector<Special> const v2{3, zero_init<Special>()};

    std::cout << v[0] << ", " << v[1] << ", " << v[2] << "\n";
    std::cout << v2[0].data << ", " << v2[1].data << ", " << v2[2].data << "\n";
    return 0;
}

As mentioned, the result is surprising was explained by dyp, we have to be careful not use std::initializer<int> by mistake and then read past the end : 如上所述, 结果 是令人惊讶的 是由dyp解释,我们必须小心不要错误地使用std::initializer<int>然后读取结束

3, 0, 0
Empty, Empty, Empty

Curly brace initialization yields a different result than regular brace initialization ( see here ) which gives the expected 0, 0, 0 . 卷曲大括号初始化产生的结果与常规大括号初始化( 参见此处 )不同,后者给出了预期的0, 0, 0 So you have to use regular braces. 因此,您必须使用常规的花括号。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM