最近,我发现void __builtin_assume(bool)了 clang,它可以向编译器提供有关程序状态的附加信息。这可以产生巨大的差异,例如:
#include <cstddef>
// compiles to about 80 instructions at -O3
unsigned sum(unsigned data[], size_t count) {
    unsigned sum = 0;
    for (size_t i = 0; i < count; ++i) {
        sum += data[i];
    }
    return sum;
}
// compiles to about 10 instructions at -O3
unsigned sum_small(unsigned data[], size_t count) {
    __builtin_assume(count <= 4);
    unsigned sum = 0;
    for (size_t i = 0; i < count; ++i) {
        sum += data[i];
    }
    return …Run Code Online (Sandbox Code Playgroud) 我试图将一个指向谓词函数的指针传递给Foo和Bar函数。该Bar函数工作正常,但该Foo函数引发编译时错误:
错误:没有匹配的调用函数
Foo<int>(bool (&)(int))
为什么编译器会引发错误?' 解包后' Foos 和Bar's 模板参数类型之间有什么区别Args吗?
#include <functional>
bool predicate(int a) {
    return (a > 5);
}
// sizeof...(Args) == 1 and I suppose it is int
template<typename... Args>
void Foo(std::function<bool(Args...)> predicate) {
    // clang: note: candidate template ignored:
    //        could not match 'function<bool (int, type-parameter-0-0...)>' 
    //        against 'bool (*)(int)'
}
template<typename Args>
void Bar(std::function<bool(Args)> predicate) {
}
int main(int argc, char const *argv[]) …Run Code Online (Sandbox Code Playgroud) c++ templates type-inference variadic-templates std-function
我想制作一个NDArray具有固定尺寸的模板,但可以在每个尺寸上调整大小。
我的问题是如何让它能够根据使用了多少对来推断构造函数中的尺寸{}?构造函数中的元素将用于初始化一些元素。
#include <array>
#include <iostream>
template<typename T, size_t Dimension>
class NDArray
{
    T* buffer = nullptr; //flattened buffer for cache locality
    std::array<size_t, Dimension> dimension;    //keep the current sizes of each dimension
public:
    NDArray(std::initializer_list<T> elements) : dimension{elements.size()}   //for 1D
    {
        std::cout << "Dimension = " << Dimension << '\n';
    }
    NDArray(std::initializer_list<NDArray<T, Dimension-1>> list) //how to make this works???
    {
        std::cout << "Dimension = " << Dimension << '\n';
    }
};
template<typename T, size_t N>
NDArray(const T(&)[N]) …Run Code Online (Sandbox Code Playgroud) 考虑unsigned char v进行一系列按位运算并将结果存储回 的情况v。在底层,它被整数提升一次,经历一系列操作,结果被截断并存储回v。
然而std::byte v,对于每个操作,操作数首先被提升为整数,进行操作,并且(中间)结果被截断并存储回某个std::byte。这将是许多来回的提升和截断。这都是概念性的,但它会在实践中造成真正的开销吗?
// `mask` and `lshf` are of type `unsigned`
unsigned char v = ...;
v = (v & mask) << lshf; // one promotion at `&` and one truncation at `=`
Run Code Online (Sandbox Code Playgroud)
// `mask` and `lshf` are of type `std::byte` and `unsigned`
std::byte v = ...;
v = (v & mask) << lshf;
// It would be like
// byte(unsigned(byte(unsigned(v) & unsigned(mask))) << …Run Code Online (Sandbox Code Playgroud) 示例代码可以在下面或godbolt上找到。假设我们有 4 个班级:
S<T>:持有数据成员。
SCtor<T>:持有数据成员并具有模板构造函数。
SCtorMutable<T>:持有可变数据成员并具有模板构造函数。
SCtorDefault<T>:持有一个成员,有一个模板构造函数,有默认的复制/移动构造函数和默认的复制/移动赋值运算符。
所有编译器都同意这 4 个类是可以简单复制的。
如果有一个简单的包装类W<T>将上述任何一个类作为数据成员。包装类W<S...<T>>仍然是可简单复制的。
如果有另一个包装类WMutable<T>将上述类中的任何一个保存为可变数据成员。
WMutable<S...<T>>是可以简单复制的。WMutable<S<T>>是可以复制的。WMutable<SCtor...<T>>不是可简单复制构造的,因此也不是可简单复制的。WMutable<S<T>>是可以简单复制的。WMutable<SCtor...<T>>不是可简单复制构造的,而是可简单复制的。应该WMutable<T>是可以简单复制的吗?
#include <type_traits>
#include <utility>
template<typename T>
struct S {
    T m_t;
};
template<typename T>
struct SCtor {
    T m_t;
    template<typename... U>
    SCtor(U&&... u): m_t(std::forward<U>(u)...) {}
};
template<typename T>
struct SCtorMutable {
    mutable T m_t;
    template<typename... U> …Run Code Online (Sandbox Code Playgroud) c++ mutable copy-constructor language-lawyer trivially-copyable
我查看了 的源代码String,发现它是根据 实现的Vec,它没有任何形式的小对象优化:
pub struct String {
    vec: Vec<u8>,
}
Run Code Online (Sandbox Code Playgroud)
来自 C++,其中每个主要标准库都使用短字符串优化 (SSO) std::string,这是非常令人惊讶的。字符串的许多用例都涉及非常短的字符串,例如:
"==", "pub","delete"setting: enabled鉴于此,默认情况下不使用任何 SSO 的理由是什么String?是否可以追溯添加该功能?是否有任何分析数据可以证明 SSO 是否有帮助?
SSO 是通过重用容器的内存来完成的std::string,否则容器将存储指针、大小和容量来存储:
也有可能只重用容量,并且有一个指向字符串对象内部的指针。所有这些通常都是通过 , 完成的union,并且在 Rust 中也是可能的。
cppreference页面[[assume]]说:
Run Code Online (Sandbox Code Playgroud)[[assume( expression )]][...] 表达式没有被求值(但它仍然可能被求值)。
这个措辞让我很困惑。这里 cppreference 错了吗?如果不评估,为什么可能会评估?它不是像 中的表达式一样未计算的操作数吗sizeof?如果不可以,后果是什么?
下面的代码在同一个翻译单元中,并且是A::v在之后定义的x,为什么A::v没有初始化为“ok”?
#include <string>
#include <iostream>
std::string foo() {
    return "OK";
}
std::string x = foo();
struct A {
    static inline std::string v = x;
};
int main() {
    std::cout << A::v << std::endl; // didn't print "OK", why?
}
Run Code Online (Sandbox Code Playgroud) c++ static-members initialization-order language-lawyer c++17
是否可以有一个带有可选模板参数的类,可以像这样调用?:
#include <iostream>
template <typename T = void>
class A final
{
public:
    // This class can be called only when T exists.
    void f()
    {
        printf("%d\n", member);
    }
    // This method can be called only when T is missing.
    void g()
    {
        printf("No template parameter\n");
    }
public:
    T member;
};
int main()
{
    A<int> a1;
    A a2;
    a1.f(); // should be valid
    a1.g(); // should be invalid, cannot compile
    a2.f(); // should be invalid, cannot compile
    a2.g(); // should …Run Code Online (Sandbox Code Playgroud) 考虑声明一个不受约束的ns::operator*. using namespace ns在块作用域中并调用函数之后foo<T>,clangns::operator*在读取内部基于范围的循环的迭代器时使用foo。没有其他类型ns涉及其他类型,因此 ADL 应该不会产生任何候选者。
在以下示例中,static_assert失败并显示消息:
错误:由于要求 'std::is_same_v<const int &, const custom_type &>',静态断言失败
汇编代码显示这ns::operator*是由 clang 使用的。gcc 和 msvc 的断言都通过了!
namespace ns {
template <typename T>
constexpr auto operator*(T&& /*value*/) {
    struct custom_type {};
    return custom_type{};
};
}  // namespace ns
template <typename T>
constexpr void foo() {
    std::vector<T> vec{};
    for (const auto& curr : vec) {
        static_assert(std::is_same_v<const T&, decltype(curr)>);
    }
}
int main() { …Run Code Online (Sandbox Code Playgroud) c++ ×9
templates ×4
c++17 ×3
assumption ×2
attributes ×1
built-in ×1
c++-concepts ×1
c++20 ×1
c++23 ×1
clang ×1
compiler-bug ×1
gcc ×1
mutable ×1
namespaces ×1
rust ×1
std-byte ×1
std-function ×1
string ×1
using ×1