考虑以下程序:
#include <cstddef>
#include <cstdio>
void f(char const*&&) { std::puts("char const*&&"); } // (1)
void f(char const* const&) { std::puts("char const* const&"); } // (2)
template <std::size_t N>
void f(char const (&)[N]) { std::puts("char const(&)[N]"); } // (3)
int main()
{
const char data[] = "a";
f(data);
}
Run Code Online (Sandbox Code Playgroud)
哪个f应该叫?为什么?
三个编译器的最新发布版本不同意这个问题的答案:
在不同的C++ 0x草案中,重载决策规则是否发生了很大变化?或者,这两个编译器真的完全错了吗?哪个重载是根据最新的C++ 0x草案选择的正确重载?
#include <iostream>
using namespace std;
void func(int (&ref)[6]) { cout << "#1" << endl; }
void func(int * &&ref) { cout << "#2" << endl; }
int main()
{
int arr[6];
func(arr); // g++(5.4): ambiguous, clang++(3.8): #2, vc++(19.11): #1
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这两个函数都是完全匹配.以下是标准的引用:
标准转换序列S1是比标准转换序列S2更好的转换序列
...
S1和S2是引用绑定(8.5.3),并且都不引用没有ref-qualifier声明的非静态成员函数的隐式对象参数,S1将rvalue引用绑定到rvalue,S2绑定左值引用.
这不是意味着第二个更好吗?
更新:
有一个相关的问题.以下代码是它的简化版本.
#include <iostream>
using namespace std;
void func(int *&) { cout << "#1" << endl; }
void func(int *&&) { cout << "#2" << endl; }
int main()
{
int arr[6]; …Run Code Online (Sandbox Code Playgroud) 我有关于c ++函数匹配优先级的简单问题.假设我有这样的代码:
#include <iostream>
void func(const char*)
{
std::cout << "const char*" << std::endl;
}
template<int N>
void func(const char (&) [N])
{
std::cout << "const char (&) [N]" << std::endl;
}
int main(int argc, char* argv[])
{
func("Hello world");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
代码的结果是(with Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM 3.6.0svn)):
const char*
Run Code Online (Sandbox Code Playgroud)
我认为"Hello world"应该是文字类型const char[].为什么const char*版本的优先级高于const char (&)[]版本?
我希望能够在重载分辨率中区分数组和指针:
class string {
public:
string(const char* c_str);
template<int N>
string(const char (&str) [N]);
};
int main() {
const char* c_str = "foo";
string foo(c_str); // ok will call string(const char*)
string bar("bar"); // call string(const char*) instead of the array version
}
Run Code Online (Sandbox Code Playgroud)
到目前为止我发现的最好的是使用指针的引用而不是指针:
class string {
public:
string(const char*& c_str);
template<int N>
string(const char (&str) [N]);
};
int main() {
const char* c_str = "foo";
string foo(c_str); // ok will call string(const char*)
string bar("bar"); // ok, will call …Run Code Online (Sandbox Code Playgroud) 为什么第一个函数call(cm(car);)绑定到第一个函数?
我知道第二个调用绑定到第二个函数,因为它是非模板,尽管两者都是完美的匹配.
如果第一个函数被定义为具有固定数组长度的非模板,则:
void cm(const char (&h)[8]) {cout << "const char (&)[8]" << endl;}
Run Code Online (Sandbox Code Playgroud)
而不是它再次被选中在第二个(第二个调用将是不明确的那种方式).
码:
template<size_t N> void cm(const char (&h)[N])
{std::cout << " const (&)[N] " << endl;}
void cm(const char * h)
{cout << " const char * " << endl;}
int main()
{
char car[] = "errqweq";
const char ccar[] = "errqweq";
cm(car);
cm(ccar);
}
Run Code Online (Sandbox Code Playgroud)
输出:
const (&)[N]
const char *
Run Code Online (Sandbox Code Playgroud) 如果这是重复我道歉.我环顾四周,发现了类似的问题,但没有完全像这样.
如果我像这样实例化模板......
MyClass<int[10]> c;
Run Code Online (Sandbox Code Playgroud)
如何编写模板以访问类型和数组大小?我已经尝试了我能想到的一切,但我无法得到它.
我受到std :: function模板的启发,它允许你使用与函数原型相似的语法,比如......
std::function<int(MyClass&)> myfunc;
Run Code Online (Sandbox Code Playgroud)
所以我认为对阵列及其大小有类似的东西会很好.我可以使用任何最新的c ++特性(c ++ 11/14).
假设我们有一个可以将东西写入输出的类
class Writer
{
public:
int write(const std::string& str);
int write(const char* str, int len);
//...
};
Run Code Online (Sandbox Code Playgroud)
我很好,这很灵活,所有这一切,直到我意识到
char* buf = new char[n]; //not terminated with '\0'
//load up buf
Writer w;
w.write(buf); //compiles!
Run Code Online (Sandbox Code Playgroud)
那是一个非常讨厌的错误.
我们可以用一些模板来修改
class WriterV2
{
public:
int write(const std::string& str);
int write(const char* str, int len);
template<typename... Args>
int write(const char*, Args...)
{ static_assert(sizeof...(Args) < 0, "Incorrect arguments"); }
//...
};
Run Code Online (Sandbox Code Playgroud)
但这种方法存在问题
WriterV2 w;
w.write("The templating genius!"); //compile error
Run Code Online (Sandbox Code Playgroud)
我该怎么办?什么是更好的设计?
在任何人问之前,重载const char (&)[N] …
我想<<为任何人写一个泛型,range最后我得到了这个:
std::ostream& operator << (std::ostream& out, std::ranges::range auto&& range) {
using namespace std::ranges;
if (empty(range)) {
return out << "[]";
}
auto current = begin(range);
out << '[' << *current;
while(++current != end(range)) {
out << ',' << *current;
}
return out << ']';
}
Run Code Online (Sandbox Code Playgroud)
像这样测试:
int main() {
std::vector<int> ints = {1, 2, 3, 4};
std::cout << ints << '\n';
}
Run Code Online (Sandbox Code Playgroud)
它完美运行并输出:
Run Code Online (Sandbox Code Playgroud)[1,2,3,4]
但是,当测试时:
int main() {
std::vector<int> empty = {};
std::cout << empty …Run Code Online (Sandbox Code Playgroud) 我正在阅读 的文档std::string_view,我注意到这些是构造函数:
constexpr basic_string_view() noexcept;
constexpr basic_string_view(const basic_string_view& other) noexcept = default;
constexpr basic_string_view(const CharT* s, size_type count);
constexpr basic_string_view(const CharT* s);
Run Code Online (Sandbox Code Playgroud)
为什么他们不介绍这个呢?
template<std::size_t n>
constexpr basic_string_view(const CharT(&s)[n]) : basic_string_view(s, n) {}
Run Code Online (Sandbox Code Playgroud)
在大多数情况下,它将节省对 的调用strlen()。有什么原因没有被引入吗?
情况1
以下代码在MSVC和GCC中产生截然不同的结果:
#include <iostream>
template <typename T>
void foo(const T&) {
#ifdef _MSC_VER
std::cout << "foo(const T&): " << __FUNCDNAME__ << std::endl;
#else
std::cout << __PRETTY_FUNCTION__ << std::endl;
#endif
}
void foo(const char*) {
std::cout << "foo(const char*)" << std::endl;
}
int main() {
extern char s[];
foo(s);
}
char s[] = "abc";
Run Code Online (Sandbox Code Playgroud)
MSVC 2013 Update 5,MSVC 2015 Update 1(也在http://webcompiler.cloudapp.net上尝试了更新2,结果相同):
foo(const char*)
Run Code Online (Sandbox Code Playgroud)
GCC 5.3.0,Clang 3.7.0(DEMO):
void foo(const T&) [with T = char []]
Run Code Online (Sandbox Code Playgroud)
案例2
现在让我们删除模板:
#include …Run Code Online (Sandbox Code Playgroud) c++ ×10
arrays ×2
c++11 ×2
c++17 ×2
function ×2
templates ×2
c++-concepts ×1
c++20 ×1
clang++ ×1
g++ ×1
interface ×1
overloading ×1
pointers ×1
reference ×1
std-ranges ×1
visual-c++ ×1