C++ Templates

【C++ Templates(8)】编译期编程

2018-04-22  本文已影响68人  downdemo

模板元编程

template<unsigned p, unsigned d>   // p: number to check, d: current divisor
struct DoIsPrime {
    static constexpr bool value = (p%d != 0) && DoIsPrime<p,d-1>::value; 
}; 

template<unsigned p>               // end recursion if divisor is 2
struct DoIsPrime<p,2> {
    static constexpr bool value = (p%2 != 0);
}; 

template<unsigned p>               // primary template
struct IsPrime {
    // start recursion with divisor from  p/2:
    static constexpr bool value = DoIsPrime<p,p/2>::value;
}; 

// special cases (to avoid endless recursion with template instantiation):
template<>
struct IsPrime<0> { static constexpr bool value = false; }; 
template<>
struct IsPrime<1> { static constexpr bool value = false; }; 
template<>
struct IsPrime<2> { static constexpr bool value = true; }; 
template<>
struct IsPrime<3> { static constexpr bool value = true; }; 
IsPrime<9>::value
// 扩展为
DoIsPrime<9,4>::value
// 扩展为
9%4!=0 && DoIsPrime<9,3>::value
// 扩展为
9%4!=0 && 9%3!=0 && DoIsPrime<9,2>::value
// 扩展为
9%4!=0 && 9%3!=0 && 9%2!=0
// 因为9%3为0,结果为false

使用constexpr计算

constexpr bool
doIsPrime (unsigned p, unsigned d)  // p: number to check, d: current divisor
{
    return d!=2 ? (p%d!=0) && doIsPrime(p,d-1) // check this and smaller divisors
              : (p%2!=0);                    // end recursion if divisor is 2
}

constexpr bool isPrime (unsigned p)
{
    return p < 4 ? !(p<2)             // handle special cases
               : doIsPrime(p,p/2);  // start recursion with divisor from p/2
}
constexpr bool isPrime (unsigned int p)
{
    for (unsigned int d=2; d<=p/2; ++d) {
        if (p % d == 0) {
            return false;  // found divisor without remainder
        }
    }
    return p > 1;      // no divisor without remainder found
}
isPrime(9)
constexpr bool b1 = isPrime(9); // 编译期计算
const bool b2 = isPrime(9); // 同样在编译期计算
// 在块作用域中编译器会决定在编译期还是运行期计算
bool fiftySevenIsPrime() {
    return isPrime(57); // 在编译期或运行期计算
}
// 下面无论x是否为质数都会在运行期计算
int x;
...
std::cout << isPrime(x); // 运行期计算

使用局部特化的执行路径选择

// 基本辅助模板
template<int SZ, bool = isPrime(SZ)>
struct Helper;

// 如果SZ不是质数的实现
template<int SZ>
struct Helper<SZ, false>
{
    ...
};

// 如果SZ是质数的实现
template<int SZ>
struct Helper<SZ, true>
{
    ...
};

template<typename T, std::size_t SZ>
long foo (std::array<T,SZ> const& coll)
{
    Helper<SZ> h; // 实现依赖于数组大小是否为质数
    ...
}
// 基本辅助模板(如果没有特化匹配)
template<int SZ, bool = isPrime(SZ)>
struct Helper
{
    ...
};

// 如果SZ是质数的实现
template<int SZ>
struct Helper<SZ, true>
{
    ...
};

SFINAE(Substitution Failure Is Not An Error)

// number of elements in a raw array:
template<typename T, unsigned N>
std::size_t len (T(&)[N])
{
    return N;
}

// number of elements for a type having size_type:
template<typename T>
typename T::size_type len (T const& t)
{
    return t.size();
}
int a[10];
std::cout << len(a); // OK: only len() for array matches
std::cout << len("tmp"); // OK: only len() for array matches
std::vector<int> v;
std::cout << len(v); // OK: only len() for a type with size_type matches
int* p;
std::cout << len(p); // ERROR: no matching len() function
found
std::allocator<int> x;
std::cout << len(x); // ERROR: len() function found, but can't
size()
// number of elements in a raw array:
template<typename T, unsigned N>
std::size_t len (T(&)[N])
{
    return N;
}

// number of elements for a type having size_type:
template<typename T>
typename T::size_type len (T const& t)
{
    return t.size();
}

// fallback for all other types:
std::size_t len (...) // 这个省略号是参数包
{
    return 0;
}

int a[10];
std::cout << len(a); // OK: len() for array is best match
std::cout << len("tmp"); //OK: len() for array is best match
std::vector<int> v;
std::cout << len(v); // OK: len() for a type with size_type is best match
int* p;
std::cout << len(p); // OK: only fallback len() matches
std::allocator<int> x;
std::cout << len(x); // ERROR: 2nd len() function matches
best, but can't call size() for x
namespace std {
    class thread {
    public:
        ...
        template<typename F, typename... Args>
        explicit thread(F&& f, Args&&... args);
        ...
    };
}
Remarks: This constructor shall not participate in overload resolution if decay_t<F> is the same type as std::thread.
namespace std {
    class thread {
    public:
        ...
        template<typename F, typename... Args,
            typename =
        std::enable_if_t<!std::is_same_v<std::decay_t<F>,
thread>>>
        explicit thread(F&& f, Args&&... args);
        ...
    };
}
template<typename T>
typename T::size_type len (T const& t)
{
    return t.size();
}

std::allocator<int> x;
std::cout << len(x) << '\n'; // ERROR: len() selected, but
x has no size()
template<typename T>
auto len (T const& t) -> decltype( (void)(t.size()), T::size_type() )
{
    return t.size();
}

编译期if

template<typename T, typename... Types>
void print (T const& firstArg, Types const&... args)
{
    std::cout << firstArg << '\n';
    if constexpr(sizeof...(args) > 0) {
        print(args...); // code only available if sizeof...(args)>0 (since C++17)
    }
}
template<typename T>
void foo(T t)
{
    if constexpr(std::is_integral_v<T>) {
        if (t > 0) {
            foo(t-1); // OK
        }
    }
    else {
        undeclared(t); // error if not declared and not discarded (i.e. T is not integral)
        undeclared(); // error if not declared (even if discarded)
        static_assert(false, "no integral"); // always asserts (even if discarded)
        static_assert(!std::is_integral_v<T>, "no integral"); //OK
    }
}
int main()
{
    if constexpr(std::numeric_limits<char>::is_signed) {
        foo(42); // OK
    }
    else {
        undeclared(42); // error if undeclared() not declared
        static_assert(false, "unsigned"); // always asserts(even if discarded)
        static_assert(!std::numeric_limits<char>::is_signed, "char is unsigned"); //OK
    }
}
template<typename T, std::size_t SZ>
void foo (std::array<T,SZ> const& coll)
{
    if constexpr(!isPrime(SZ)) {
        ... //special additional handling if the passed array
        // has no prime number as size
    }
    ...
}
上一篇 下一篇

猜你喜欢

热点阅读